-
Notifications
You must be signed in to change notification settings - Fork 0
[2주차 기본/심화/공유 과제] 관리자 페이지 #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
ocahs9
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이번 과제 쉽지 않았죠.. 고생하셨습니다 !!
정말 열심히 하셨네요 80시간... 대단해요 👍👍
코멘트 한번 확인해주시고, 리팩토링도 도전해봅시다 화이팅 !!!
| <tbody> | ||
| <!-- 한 row에 들어가는 정보 값. 나중에 js 할 때 <tr>을 추가하는 식으로 해야됨 --> | ||
| </tbody> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주석 굿! 사실 이번에는 tbody가 딱 하나뿐이라서 상관 없을 수도 있는데, 나중에는 더 확실하게 가져오기 위해서 id를 부여해준 뒤, 해당 id를 바탕으로 js에서 해당 태그를 뽑아오는 게 좋아요!
예를 들면, getElementById('아이디') 와 같은 방식으로요! 그래서 나중에는 언젠가 js에서 뽑아와서 사용할 요소라면, id를 부여하는 습관을 가져봅시다 !
| <header class="header"> | ||
| <a href="#"><i class="fa-solid fa-bars"></i></a> | ||
| <h1>물결웹팟 파트원 관리 페이지</h1> | ||
| <span>made by AND SOPT 메이커스팀장 김가연🌊🚀</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍👍
| // 변수에 값이 있을 시, 필터에 적힌 값과 멤버 배열에 담긴 값을 비교해서, filteredMembers로 반환한다! | ||
| if (nameFilter) { | ||
| filteredMembers = filteredMembers.filter(member => member.name === nameFilter); | ||
| } | ||
| if (engNameFilter) { | ||
| filteredMembers = filteredMembers.filter(member => member.englishName === engNameFilter); | ||
| } | ||
| if (githubIDFilter) { | ||
| filteredMembers = filteredMembers.filter(member => member.github == githubIDFilter); | ||
| } | ||
| if (genderFilter) { | ||
| filteredMembers = filteredMembers.filter(member => member.gender === genderFilter); | ||
| } | ||
| if (roleFilter) { | ||
| filteredMembers = filteredMembers.filter(member => member.role === roleFilter); | ||
| } | ||
| if (firstWeekGroupFilter) { | ||
| filteredMembers = filteredMembers.filter(member => member.firstWeekGroup == firstWeekGroupFilter); | ||
| } | ||
| if (secondWeekGroupFilter) { | ||
| filteredMembers = filteredMembers.filter(member => member.secondWeekGroup == secondWeekGroupFilter); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
각각의 필터에 있는 값은 위에서 뽑아온 뒤에, 여기 if문으로 비교하고 해당 필터의 값과 일치하는 값을 갖는 멤버를 뽑아내는 로직이죠? 근데 이렇게 구성하면, 문제가 몇가지 있어요!
-
만약 nameFilter의 value와 같은 멤버와, secondWeekGroupFilter 의 value의 같은 멤버가 동시에 존재한다면?
-> 결국 secondWeekGroupFilter 의 value에 해당하는 멤버들만 남게 된다.
-> 이유는? if문은 항상 실행되는데, filteredMembers 라는 딱 하나의 변수라서 결국 해당 변수에는 마지막에 실행된 값만 들어가게 되므로 ! -
필터링 중복 적용이 안될겁니다. 물론 이건 심화 과제였던 것 같은데, 배우면 좋을 것 같아서 알려드릴게요!
그래서 '모든 필터링'을 만족할 때의 멤버를 보여주고 싶다면 && 연산자로 연결해서 구현해보는 것도 좋을 것 같아요.
그리고 추가로 하나 더 말씀드려보자면, 보통 종속적인 데이터들은(같은 레벨에서 관리되는 데이터들은) if-else if- else 문으로 관리되지 if문 여러개로 관리되지 않아요! if문 여러개는 보통 논리적으로 다른 기능을 표시할 때 사용하곤 합니다 ~! 참고해서 리팩토링 도전해봅시다!
| modalOpenBtn.addEventListener('click',function(){ | ||
| //'on' class 추가 | ||
| modal.classList.add('on'); | ||
| }); | ||
|
|
||
| //닫기 버튼을 눌렀을 때 모달팝업이 닫힘 | ||
| modalCloseBtn.addEventListener('click',function(){ | ||
| //'on' class 제거 | ||
| modal.classList.remove('on'); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class로 관리해주는 것도 좋은데, 아주 간단하게 모달의 on/off만 나타낼거니까
modal.style.display = "none";
modal.style.display = "flex";
과 같은 방식을 이용해보는 건 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style 속성을 수정하는 것도 하나의 방법이긴 한데,
저는 개인적으로 css, javascript의 분리를 위해서 javascript 파일에서 css 속성을 건드리는 것을 선호하지 않는데
혹시 이부분에 대해서는 어떻게 생각하시나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
css 속성을 직접 건드리지 않는다는거면, classList.add / classList.remove 와 같은 방식을 사용하신다는 말씀이시죠?
사실 분리하는 것도 좋다고 생각하긴 하는데, 저는 여러 파일을 왔다 갔다하는게 오히려 시간을 쓰는 것 같아서.. 좋지 않더라구요. 그래서 간단한 문법 같은 경우에는 그냥 css를 직접 건드리는 방식을 채택하는 편이긴 합니다..! (물론 좀 긴 css를 넣었다 뺐다 해야하는 경우에는 class를 추가,제거 해주는 방식이 더 좋긴 할 것 같습니다 !)
확실히 본인의 취향에 따라 달라질 수도 있을 것 같아요! 좋은 의견 감사합니다 👍
| //하나라도 해제되면 전체 선택 체크박스 해제해야함...ㅠㅠㅠ!! | ||
| document.querySelector('#checkAll'); | ||
| checkAll.addEventListener('click', function(){ | ||
|
|
||
| const isChecked = checkAll.checked; | ||
|
|
||
| if(isChecked){ | ||
| const checkboxes = document.querySelectorAll('input[type="checkbox"]'); | ||
| for(const checkbox of checkboxes){ | ||
| checkbox.checked = true; | ||
| } | ||
| } | ||
|
|
||
| else{ | ||
| const checkboxes = document.querySelectorAll('input[type="checkbox"]'); | ||
| for(const checkbox of checkboxes){ | ||
| checkbox.checked = false; | ||
| } | ||
| } | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하나라도 체크 해제되거나, 전부 체크가 되면 전체 체크 버튼이 비활성화/활성화가 자동으로 되어야했는데 그게 쉽지 않았죠.. 저는 전체체크박스들을 제외한 다른 체크박스들에 같은 class를 주고, 그걸 전부 querySelectorAll 로 뽑아온 뒤 값을 확인해서 전체 체크박스의 checked를 바꿔주는 방식으로 구현했는데, 제 코드 한번 참고해보시는 것도 좋을 것 같아요!
minjeoong
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
70시간.. 너무 고생하셨습니다!!!
여러 섬세한 부분 챙겨야할 게 많아서 어려웠던 것 같아요 🥺
코드 보면서 또 배웠던 것 같네요 ... 너무 고생하셨습니다!
| //setItem | ||
| //이미 불러와져있는 data가 없으면 setItem 함수 호출 | ||
| if (!localStorage.getItem('data')) { | ||
| localStorage.setItem('data', JSON.stringify(members)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
엄청 중요한 부분은 아니지만,, 코드가 중복되어 사용되고 있어서
밑에 가연님이 만들어두신 다음 함수를 호출하면 깔끔할 것 같아요!
function saveMembers(members){
localStorage.setItem('data', JSON.stringify(members));
}
| const nameFilter = document.querySelector('#filter-name').value; | ||
| const engNameFilter = document.querySelector('#filter-engName').value; | ||
| const githubIDFilter = document.querySelector('#filter-githubID').value; | ||
| const genderFilter = document.querySelector('#filter-gender').value; | ||
| const roleFilter = document.querySelector('#filter-role').value; | ||
| const firstWeekGroupFilter = document.querySelector('#filter-week1').value; | ||
| const secondWeekGroupFilter = document.querySelector('#filter-week2').value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
id 는 class 보다 명시도가 높아요
(-> 관련 링크 첨부드립니다! https://developer.mozilla.org/ko/docs/Web/CSS/Specificity )
특별히 다른 값에 주로 주는 요소로 주로 사용되기 때문에
#id 보다는, class 를 자주 사용하면 좋습니다!
CSS 먹일 때와 동시에 생각해서 구현하면 좋을 것 같아요
"권장사항"이지 필수는 아닙니다!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| row.remove(); // row 삭제 | ||
| }); | ||
|
|
||
| saveMembers(members); // 변경된 멤버 데이터 로컬 스토리지에 저장하기 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오,, 함수를 따로 빼서 코드가 깔끔해보이고
직관적이여서 너무 좋은 것 같습니다 👏🏻👏🏻👍🏻
|
|
||
| //초기화 버튼 구현 | ||
| document.querySelector('#reset').addEventListener('click', function() { | ||
| document.querySelectorAll('.text-box').forEach(input => input.value = ''); // 텍스트 입력 필드 초기화 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오 저는 하나하나 다 초기화 했던 것 같은데,
이런 방법이 좀 더 깔끔하고 좋은 것 같네요
굿 👏🏻👏🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
input을 초기화하는 좋은 방법인 것 같습니다.
이 방법 이외에도, form태그를 사용한다면, form안의 button 태그에 type='reset' 속성을 사용해서 form 안의 모든 input을 초기화 시켜주는 방법도 있으니 참고해보시면 좋을 것 같습니다!
| } | ||
|
|
||
|
|
||
| console.log(filteredMembers); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
불필요한 콘솔로그는 지우는 게 좋습니다!
리팩터링 할 때 수정하면 좋을 것 같아요 🫠
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞습니다~
개발 과정에서 console.log로 값을 확인하고 테스트 하는 습관은 너무 좋지만, 개발/테스트 후에는 지우기~!
| </div> | ||
| <div class="filter-text-box week1"> | ||
| <div class="filter-title">1주차 금잔디조</div> | ||
| <input type="text" class="text-box" id="filter-week1" placeholder="1주차 금잔디조를 입력해주세요(1~9)."> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type 을 text 로 정해두면,
실제로 10 이상 값도 물론이고, string 값 까지 전부 받을 수 있는 상태가 되어요!
그래서, type 은 number 로 지정해주는 게 좋을 것 같아요
그리고 타입이 number 이기 때문에
요소의 최솟값을 명시하는 min 속성과
요소의 최댓값을 명시하는 max 속성을 사용해보는 것 도 좋습니다.
관련 아티클 첨부해 두겠습니다!
https://www.tcpschool.com/html-tag-attrs/input-min
https://tcpschool.com/html-tag-attrs/input-max
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| font-display: swap; | ||
| } | ||
|
|
||
| html { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음부터 1rem = 10px 설정하고 들어가는 거 너무 좋네요.. 🍀
gudusol
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이미 레전드 개발자 두분이 고봉밥 코리 잔뜩 남겨주셨네요!
2주차 과제 하시느라 고생많으셨습니다. 메팀장, 임원진 일도 많으실텐데 세미나 과제도 열심히 임해주셔서 더 감사합니다 ㅎㅎ
javascript 코드도 잘 작성하셨고, 함수화도 많이 시키면서 깔끔한 코드 작성하려고 많이 노력한게 보이는 것 같습니다!
3주차 과제도 화이팅~! 🔥
| <div class="filter-title">이름</div> | ||
| <input type="text" class="text-box" id="filter-name" placeholder="이름을 입력해주세요."> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기는 div태그보다는 label태그를 사용해보는건 어떨까요?
label과 input태그를 함께 사용할 때 (input아니어도 괜찮습니다),
input에 id를 설정하고, 해당 id를 label 태그의 for 속성으로 연결해주면 여러 가지 장점이 있습니다!
- 라벨을 클릭했을때 input이 자동으로 활성화됨으로써 사용성 좋아짐.
- 어떤 라벨이 어떤 요소에 연결되는지 정확히 인식할 수 있어, 접근성이 향상.
- 유지보수와 가독성 측면에서도 어떤 label이 어떤 요소를 위한 것인지 한눈에 알 수 있어 더 좋음.
다양한 장점이 있으니 label을 사용하실 때는 for속성을 꼭 한번씩 생각해보시면 좋을 것 같습니다!
| </div> | ||
| <div class="filter-text-box week1"> | ||
| <div class="filter-title">1주차 금잔디조</div> | ||
| <input type="text" class="text-box" id="filter-week1" placeholder="1주차 금잔디조를 입력해주세요(1~9)."> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| <button class="secondary filter-button" id="reset">초기화</button> | ||
| <button class="primary filter-button" id="search">검색</button> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
button 태그의 기본 type 속성은 type="submit"로 설정되어 있습니다. 이 때문에 button이 form 태그 안에 있을 경우 클릭 시 기본적으로 폼이 제출되고, 새로고침이 발생합니다. 그리고 필요에 따라 이 기본동작(새로고침)을 막기 위해 이벤트 핸들러에서 preventDefault()를 사용하게 됩니다.
따라서 코드의 명확성과 의도 전달을 위해 form태그의 외부의 버튼(제출용이 아닌 버튼)이라면 type="button"을 명시적으로 설정하는 것이 권장됩니다! 이렇게 하면 해당 버튼이 제출용이 아님을 분명히 할 수 있어, 코드 이해와 유지보수에 유리합니다.
사실 button이 type="submit" 속성을 가지고 있더라도, form 태그 외부에 있을 때는 새로고침 없이 단순 버튼처럼 동작하기는 한다고 하지만, type='button'을 이용해 좀 더 명시적으로 submit용 버튼이 아님을 나타내 주면 더 좋을 것 같습니다!
| </main> | ||
| </section> | ||
|
|
||
| <script type="module" src="admin.js"></script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modal은 화면에 바로 그려지는 부분이 아니라 이렇게 script태그가 중간에 있어도 큰 영향은 없을것 같긴한데, 일반적으로 js파일을 불러오는 script태그는 body태그의 가장 하단에 위치시키는 것을 권장하고 있습니다!
그 이유는,
브라우저가 html을 읽다가 script태그를 만나면 화면 그리는 것을 멈추고, js파일을 읽게 됩니다.
또한 js 파일에서 DOM 요소에 접근하게 된다면, 화면이 모두 그려져 있어야 정상적으로 요소에 접근할 수 있습니다.
따라서 js파일을 불러오는 script 태그는 body태그의 맨 아래에 위치시키는 것이 좋습니다. (혹은 defer속성을 사용하는 방법도 있습니다.)
관련 아티클 하나 첨부해드릴게요!
| @font-face { | ||
| font-family: 'SUIT Variable'; | ||
| src: url('https://cdn.jsdelivr.net/gh/sun-typeface/SUIT@2/fonts/variable/woff2/SUIT-Variable.woff2') format('woff2'); | ||
| font-weight: 100 900; | ||
| font-style: normal; | ||
| font-display: swap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
폰트 설정까지! 디테일 👍
| const checkboxes = document.querySelectorAll('input[type="checkbox"]'); | ||
| for(const checkbox of checkboxes){ | ||
| checkbox.checked = true; | ||
| } | ||
| } | ||
|
|
||
| else{ | ||
| const checkboxes = document.querySelectorAll('input[type="checkbox"]'); | ||
| for(const checkbox of checkboxes){ | ||
| checkbox.checked = false; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const checkboxes = document.querySelectorAll('input[type="checkbox"]');이 코드가 똑같이 두번 사용되고 있어서, if 문 위쪽에서 한번만 호출해서 사용할 수 있을 것 같습니다!
| modalOpenBtn.addEventListener('click',function(){ | ||
| //'on' class 추가 | ||
| modal.classList.add('on'); | ||
| }); | ||
|
|
||
| //닫기 버튼을 눌렀을 때 모달팝업이 닫힘 | ||
| modalCloseBtn.addEventListener('click',function(){ | ||
| //'on' class 제거 | ||
| modal.classList.remove('on'); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style 속성을 수정하는 것도 하나의 방법이긴 한데,
저는 개인적으로 css, javascript의 분리를 위해서 javascript 파일에서 css 속성을 건드리는 것을 선호하지 않는데
혹시 이부분에 대해서는 어떻게 생각하시나요?
|
|
||
| //초기화 버튼 구현 | ||
| document.querySelector('#reset').addEventListener('click', function() { | ||
| document.querySelectorAll('.text-box').forEach(input => input.value = ''); // 텍스트 입력 필드 초기화 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
input을 초기화하는 좋은 방법인 것 같습니다.
이 방법 이외에도, form태그를 사용한다면, form안의 button 태그에 type='reset' 속성을 사용해서 form 안의 모든 input을 초기화 시켜주는 방법도 있으니 참고해보시면 좋을 것 같습니다!
| const nameFilter = document.querySelector('#filter-name').value; | ||
| const engNameFilter = document.querySelector('#filter-engName').value; | ||
| const githubIDFilter = document.querySelector('#filter-githubID').value; | ||
| const genderFilter = document.querySelector('#filter-gender').value; | ||
| const roleFilter = document.querySelector('#filter-role').value; | ||
| const firstWeekGroupFilter = document.querySelector('#filter-week1').value; | ||
| const secondWeekGroupFilter = document.querySelector('#filter-week2').value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| } | ||
|
|
||
|
|
||
| console.log(filteredMembers); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞습니다~
개발 과정에서 console.log로 값을 확인하고 테스트 하는 습관은 너무 좋지만, 개발/테스트 후에는 지우기~!
✨ 구현 기능 명세
💡 기본 과제
데이터
헤더
필터
(기본과제에서는 7개 모두 input으로 구현해도 괜찮음)
표
선택삭제,추가버튼이 위치선택삭제버튼 클릭 시, 체크된 항목들 삭제추가버튼 클릭 시, 항목 추가 모달 등장모달
닫기버튼추가버튼 클릭 시, 모달 닫히면 데이터 추가🔥 심화 과제
필터
표
모달
공유과제
제목: [JavaScript] 자바스크립트의 데이터 타입에 대해.araboza
링크 첨부 : https://wave-web.tistory.com/67
❗️ 내가 새로 알게 된 점
❓ 구현 과정에서의 어려웠던/고민했던 부분
🥲 소요 시간
70h 정도...🖼️ 구현 결과물
https://youtu.be/ubH9I7XKAec