블로그
전체 14#카테고리
- 프론트엔드
#태그
- 인프런
- 인프런워밍업클럽
- 스터디0기
- 워밍업
- 워밍업클럽
- 프론트엔드
- 프론트
- FE
- 미션
- 과제
- 발자국
- this
- constructor
2024. 03. 20.
3
[인프런 워밍업클럽 스터디 FE 0기] 후기
0.반년을 일하면서 어떻게든 잘 버티고 있다고 생각했다."간단하게 인풋에 키랑 아이디 넣고 버튼 누르면 삭제되는 html이면 될 것 같아요."새폴더 > index.htmlvs code의 빈 공간이 이렇게나 낯설고 막막하게 느껴질 줄은 몰랐다. 1.나도 나를 잘 모르지만, 얼마나 게으른 사람인지는 잘 안다.어떻게든 날 밀어붙일 계기가 필요하다 생각하고 이것저것 알아보다가 인프런에서 진행하는 스터디를 알게 됐다.무작정 신청하고 들어온 스터디엔 생각보다 많은 사람이 있었고 그만큼이나 열심히 노력하시는 러너님들이 가득했다. 2.사실 스터디 초반엔 이미 다 알고 있는 부분이라 생각해 적당히 강의를 봤던 적도 있었다.그리고 첫 과제를 진행하는 순간 내가 왜 이 스터디를 신청했던 건지 몸소 느낄 수 있었다.생각보다 과제는 하루이틀만에 끝낼 수 없었고 남은 기간 동안 얼마나 할 수 있을지 계산적으로 생각하기도 했지만, 이게 무슨 의미인가 싶어 반쯤은 체념하고 대신 열심히만 하자고 마음을 먹었다.그런데 적당히 포기하고 마음을 놓으니 오히려 오기가 생겼던 것 같다.그 뒤로부터 출근하면서 강의 보고, 퇴근하면서 강의 보고, 집에 와서 과제하고, 주말에는 더 몰아서 강의 보고 과제하고...며칠, 몇 주를 그렇게 지냈다. 3.퇴근하고 디스코드에 들어가면 늦은 시간에도 늘 접속해 계셨던 몇몇 러너님들이 계셨는데 하루쯤 쉴까 싶은 날에는 그런 러너님들을 보며 나를 바로잡곤 했다.그중 한 러너님께서 사이드 프로젝트 인원을 모집한다는 글을 올리셨고, 또다시 나를 밀어붙이기 위해 과감하게 연락을 드려 좋은 기회를 가지게 됐다.이번 워밍업 스터디를 신청하길 잘했다고 가장 크게 느꼈던 점은 정말 많은 분들이 개발에 대해 공부하고 있고 실천하고 있다는 걸 눈으로 확인하고 몸소 느꼈던 부분이 아닐까 싶다.게다가 생각지도 못 했던 우수 러너가 되었다..!나를 선정해주신 코치님에게 감사한 마음과 그만큼 앞으로 더 잘해야겠다는 다짐을 다시 한번 하게 됐다.우연한 기회에 만난 인프런, 그리고 스터디가 내게 많은 전환점이 될 수 있을 것 같아 감사하다는 말씀을 전하고 싶다. 4.사실 원동력의 팔할은 나보다 더 노력하는 러너님들을 향한 시샘과 존경이었다.
프론트엔드
・
인프런
・
인프런워밍업클럽
・
스터디0기
2024. 03. 13.
1
[인프런 워밍업 클럽 FE 0기] 과제 총정리
미션1 - 음식 메뉴 앱깃허브 : 🍝food-recipe-app블로그 : https://www.inflearn.com/blogs/6660미션2 - 가위 바위 보 앱깃허브 : 👊Rock-🖐Paper-✌Scissors-app블로그 : https://www.inflearn.com/blogs/6742미션3 - 퀴즈 앱깃허브 : 🤔 quiz-app블로그 : https://www.inflearn.com/blogs/6860미션4-1 - 책 리스트 앱깃허브 : 📚 book-list-app블로그 : https://www.inflearn.com/blogs/6877미션4-2 - GitHubFinder 앱깃허브 : 🔍 github-finder-app블로그 : https://www.inflearn.com/blogs/6914미션5 - 비밀번호 생성 앱깃허브 : 🔐 Password Genrator블로그 : https://www.inflearn.com/blogs/6950미션6 - 타이핑 테스트 앱깃허브 : ⌨ Typing Test APP블로그 : https://www.inflearn.com/blogs/6967미션7 - 예산 계산기 앱깃허브 : 💸 Budget Calculator APP블로그 : https://www.inflearn.com/blogs/7104미션8 - 디즈니 플러스 앱깃허브 : 🎞 Disney Plus APP블로그 : https://www.inflearn.com/blogs/7168미션9 - 포켓몬 도감 앱깃허브 : Pokemon App
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
FE
・
미션
・
과제
・
발자국
2024. 03. 13.
0
[인프런 워밍업 클럽 FE 0기] 3주차 발자국
미션8 - 디즈니 플러스 앱깃허브 : 🎞 Disney Plus APP블로그 : https://www.inflearn.com/blogs/7168미션9 - 포켓몬 도감 앱깃허브 : Pokemon App
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
FE
・
미션
・
과제
・
발자국
2024. 03. 10.
2
[인프런 워밍업 클럽 FE 0기] 미션8 - 디즈니 플러스 앱
🎞 Disney Plus APP GitHub 🎞 Disney Plus APP DemoRecord by ScreenToGif 개요인프런 워밍업 클럽 FE 0기의 여덟 번째 미션인 '디즈니 플러스 앱' 입니다. 따라하며 배우는 리액트 섹션 4~5(리액트로 Netflix 앱 만들기) 목표swiper 라이브러리 커스텀해보기react-oauth/google 로 구글 로그인 연동해보기 구현swiper 라이브러리 커스텀해보기// LoginPage import "swiper/css/effect-fade"; {...} // Row.tsx import "swiper/css/mousewheel"; {...} 2024년 3월 10일의 디즈니 플러스 메인 페이지를 그대로 옮겨보고자 swiper 라이브러리를 커스텀해봤다.로그인 페이지에서는 좌우로 넘기는 슬라이드가 아닌 fade-in-out의 슬라이드를 구현하기 위해 swiper에 EffectFade 모듈을 추가하고 fadeEffect 속성을 추가했다.이 fadeEffect가 제대로 작동하기 위해선 반드시 해당 이펙트의 css를 추가해야 한다.다른 모듈이나 컴포넌트를 추가할 때처럼 자동으로 추가되지 않으니 주의해야 한다. (이걸 몰라서 한참을 찾았다. 😥)Row 컴포넌트는 마우스 휠에 따라 움직이는 슬라이드를 만들기 위해 Mousewheel 모듈과 속성을 이용했다.이렇게 슬라이드 속성을 정한 뒤에 swiper가 렌더링하는 요소의 class를 찾아 CSS에서 원하는 디자인으로 변경하면 된다.이때 라이브러리의 CSS와 겹치는 속성이 있을 수 있기 떄문에 '!important'를 붙이는 게 좋다. react-oauth/google 로 구글 로그인 연동해보기// index.js // App.jsx const navigate = useNavigate(); const [isLogin, setIsLogin] = useState( localStorage.getItem("user") ? true : false ); useEffect(() => { isLogin ? navigate("/") : navigate("/login"); }, [isLogin]); {isLogin ? ( }> } /> } /> } /> ) : ( } /> )} react-oauth/google는 구글 로그인을 지원하는 라이브러리로, 사전에 구글의 Cloud에서 API 등록을 하고 Client ID를 발급받아야 사용할 수 있다.먼저 프로젝트의 최상위에 GoogleOAuthProvider로 감싸준다.그리고 사용자의 로그인 여부에 따라 페이지를 이동시키기 위해 라우터를 설정한 App 컴포넌트에서 관련 코드를 작성했다.페이지가 렌더링 될 때 로컬 스토리지에 저장된 유저 정보를 받아오고 만약 없다면 로그인 페이지로 보내도록 했다. // loginPage const googleLogin = async (credentialResponse) => { localStorage.setItem( "user", JSON.stringify(jwtDecode(credentialResponse.credential)) ); setIsLogin(true); }; googleLogin(credentialResponse)} /> GoogleLogin 컴포넌트는 react-oauth/google 라이브러리에서 지원하는 버튼 컴포넌트로 디자인 및 로그인 관련 함수가 내장되어 있다.onSuccess는 사용자의 로그인이 성공했을 때 실행되는 콜백 함수이며, 인자로 로그인한 유저의 정보를 담은 데이터를 갖는다.여기서 credential이라는 값은 유저의 정보를 담고 있는 토큰으로 암호화되어 있기 때문에 jwt-decode 라이브러리를 이용해 디코딩하여 사용해야 한다.여기서 받은 picture는 사용자의 프로필 이미지 링크를 포함하고 있어서 Nav 컴포넌트에서 사용해 로그인한 유저의 프로필 이미지로 변경했다. 회고'Netflix 앱 만들기'를 하면서 사용했던 기술이 대부분이라 오래 걸리지 않을 것 같았지만...라이브러리 알아보고 문서 읽고 실행해보고... 하는 데 너무 오래 걸린 것 같다.배너 하단의 카테고리 부분은 이전에 같은 과제를 하셨던 분의 깃허브를 참고했다. (https://github.com/kimneighbor/clone-disney-plus-app)로그인 페이지는 따라하기 싫어서 현재 디즈니 플러스 홈페이지를 보고 참고했다.그대로 하면 얼마 안 걸릴 거라 생각했는데 생각보다 라이브러리 커스텀에서 좀 애를 먹었다. 😅with_networks: "2739" 2739는 TMDB에서 디즈니 플러스 방송사(networks) 코드라서 axios의 instance 기본 값에 추가했다.몇몇 요청은 해당 파라미터가 통하지 않거나 오류를 보내기도 해서 완벽하진 않다.디즈니 플러스에서 API를 제공했다면 더 알맞게 페이지를 구현할 수 있었을 텐데 하는 아쉬움이 남는다.한편 영화 정보 API를 제공해주는 TMDB(The Movie Database) 같은 곳이 있어 감사하고 다행이라는 생각이 들었다.프론트엔드 공부하는데 API를 제공해주는 곳이 아예 없었다면 혹은 매번 일정 비용을 지불해야 했다면 얼마나 힘들었을까로그인도 사실 좀 더 좋은 라우팅 구조나 상태 관리 라이브러리를 공부하고 사용해보고 싶었지만...계속 욕심만 커지는 것 같아 최대한 간단하게 구현하려 했다.(사실 과제 밀려서 조바심에 아무것도 못 했다... 😂)
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
FE
・
미션
・
과제
・
발자국
2024. 03. 06.
1
[인프런 워밍업 클럽 FE 0기] 2주차 발자국
미션4-2 - GitHubFinder 앱깃허브 : 🔍 github-finder-app블로그 : https://www.inflearn.com/blogs/6914미션5 - 비밀번호 생성 앱깃허브 : 🔐 Password Genrator블로그 : https://www.inflearn.com/blogs/6950미션6 - 타이핑 테스트 앱깃허브 : ⌨ Typing Test APP블로그 : https://www.inflearn.com/blogs/6967미션7 - 예산 계산기 앱깃허브 : 💸 Budget Calculator APP블로그 : https://www.inflearn.com/blogs/7104
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
FE
・
미션
・
과제
・
발자국
2024. 03. 06.
1
[인프런 워밍업 클럽 FE 0기] 미션7 - 예산 계산기 앱
💸 Budget Calculator APP GitHub 💸 Budget Calculator APP DemoRecord by ScreenToGif 개요인프런 워밍업 클럽 FE 0기의 일곱 번째 미션인 '예산 계산기 앱' 입니다. 따라하며 배우는 리액트 섹션 0~3(To-Do 앱) 목표의존성 배열(Dependency Array) 을 이용해 함수 실행하기state 를 전역 변수처럼(?) 사용해보기 구현구조|-- App | |-- Form | |-- Lists | | |-- List 의존성 배열(Dependency Array) 을 이용해 함수 실행하기// App.jsx const [budgetList, setBudgetList] = useState( JSON.parse(localStorage.getItem("budgetList")) || [] ); useEffect(() => { localStorage.setItem("budgetList", JSON.stringify(budgetList)); }, [budgetList]); const totalCost = useCallback(() => { return budgetList.reduce((acc, cur) => acc + cur.cost, 0); }, [budgetList]); 최상위 컴포넌트인 컴포넌트에서 만든 'budgetList'이라는 state를 useEffect와 useCallback의 의존성 배열에 추가했다.useEffect에서는 해당 state가 변경되면 로컬 스토리지의 budgetList를 최근의 리스트로 변경한다.이렇게 하면 일일이 setBudgetList가 호출되는 곳마다 함수를 사용하지 않아도 된다.다음은 예산의 총 금액을 반환하는 함수가 리스트가 변경될 때마다 실행되도록 useCallback으로 감싸고 의존성 배열에 state를 추가했다.// Form.jsx const budgetNameRef = useRef(); const [budgetName, setBudgetName] = useState(""); const [budgetCost, setBudgetCost] = useState(0); useEffect(() => { if (isEdit) { setBudgetName(budget.name); setBudgetCost(budget.cost); budgetNameRef.current.focus(); } }, [isEdit]); 컴포넌트에서는 useEffect에 'isEdit'이라는 state를 의존성 배열에 추가했다.사용자가 예산을 수정하기 위해 list의 Edit 버튼을 클릭하면 해당 budget의 name과 cost를 최근 state로 불러오고, useRef를 이용해 name을 입력하는 요소에 focus 상태가 되도록 했다.state 를 전역 변수처럼(?) 사용해보기// App.jsx const [currentBudget, setCurrentBudget] = useState({ isEdit: false, budget: {}, }); // List.jsx const handleEdit = () => { setCurrentBudget({ isEdit: true, budget: list, }); setHandleStatus({ type: "edit", message: "Editing..." }); }; // Form.jsx const handleBudgetSubmit = (e) => { const newBudget = { id: Date.now(), name: budgetName, cost: budgetCost, }; // isEdit의 값에 따라 새로 추가할지 수정할지 결정 if (isEdit) { setBudgetList((prevBudgetList) => { const newBudgetLists = [...prevBudgetList]; const index = newBudgetLists.findIndex(({ id }) => id === budget.id); newBudgetLists[index] = newBudget; return newBudgetLists; }); setCurrentBudget({ isEdit: false, budget: {} }); setHandleStatus({ type: "submit", message: "Edit Success!" }); } else { setBudgetList((prevBudgetLists) => [...prevBudgetLists, newBudget]); setHandleStatus({ type: "submit", message: "Submit Success!" }); } // submit 종료 시 input의 데이터를 초깃값으로 설정 setBudgetName(""); setBudgetCost(0); }; 배웠던 To Do 앱은 List의 Edit 버튼을 클릭했을 때 해당 List의 요소를 input 요소로 변경시키고 수정을 했다.하지만 과제는 클릭을 했을 때 List의 요소를 변경시키는 게 아니라 Form의 input에 해당 예산의 데이터를 전달해야 했다.그래서 마치 전역 변수처럼 사용할 'currentBudget'이라는 state를 생성하고 'isEdit'이라는 boolean 값과 수정할 예산의 데이터를 담을 'budget'이라는 값을 설정했다.'isEdit'의 상태 값이 true일 때 수정하기와 삭제하기 요소를 disabled로 변경한다.또한 submit 함수는 새로운 입력 값을 budgetList에 추가하지 않고 해당 예산의 index를 찾아 수정하고 리스트를 변경한다.이렇게 하니 onSubmit과 onEdit 처럼 비슷한 기능을 하는 함수를 여러 개 만들지 않아도 되었다. ⚠ setTimeout 렌더링const { type, message } = handleStatus; const handleStyle = useCallback(() => { if (type === "edit") { return "text-gray-500 block"; } else if (type === "none") { return "hidden"; } else { // 2초 뒤에 실행 --> App - Form - Status 1번 더 렌더링 setTimeout(() => { setHandleStatus({ type: "none", message: "" }); }, 2000); if (type === "submit") { return "text-green-400 block"; } else { return "text-red-400 block"; } } }, [type]); 추가, 삭제, 수정의 완료 및 진행 중 상태를 보여주는 컴포넌트를 만들었다.App에서 만든 'handleStatus'라는 state를 전달하고 메세지가 나타난 뒤에 사라지게 만들고 싶어서 setTimeout() 메서드를 이용해 2초 뒤에 상태를 초기화했다.하지만 이 상태가 App과 Form 컴포넌트에서 참고하다 보니 나타나고 사라질 때마다 렌더링이 발생했다.CSS의 opacity로 처리하기엔 state의 값을 변경해야 했기에 알맞는 방법은 아니라 생각했다.뭔가 컴포넌트 내부에서만 렌더링이 일어나게 하고 싶었는데 아직 다른 방법을 찾지 못했다.😢😢😢 회고다른 컴포넌트의 클릭 이벤트로 변경된 state를 이용하는 부분이 생각보다 오래 걸렸다.처음엔 콜백 함수처럼 App 컴포넌트에서 함수 만들고 prop으로 넘겨봤지만 List와 Form은 종속적인 관계가 아니라 힘들었다. 😢그래서 생각해낸 게 state를 이용해서 상태의 변경을 이벤트처럼 사용하는 것이었다.pub-sub 혹은 observer 패턴 같다는 생각도 했지만, 이렇게 최상위에서 선언한 state가 이곳저곳 돌아다니는 게 좋은 방법은 아닐 것 같다는 생각이 들었다.규모가 커지면 렌더링 관리도 힘들고 props를 쫓아다녀야 하기 때문이다.이래서 상태 관리 라이브러리가 나왔나 보다. 🤔
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
FE
・
미션
・
과제
・
발자국
2024. 03. 03.
0
[인프런 워밍업 클럽 FE 0기] 미션6 - 타이핑 테스트 앱
⌨ Typing Test APP GitHub ⌨ Typing Test APP DemoRecord by ScreenToGif 개요인프런 워밍업 클럽 FE 0기의 여섯 번째 미션인 '타이핑 테스트 앱' 입니다. 따라하며 배우는 자바스크립트 섹션 9(오브젝트 만들기) 목표setInterval() 메서드를 이용해 타이머 만들기split() 메서드를 이용해 문자열 관리(는 위험하다) 구현setInterval() 메서드를 이용해 타이머 만들기textarea.addEventListener('focus', gameStart); let timer = null; function gameStart() { // 만약 마우스를 다른 곳에 뒀다가 다시 focus 할 때 막기 if (timer) { return; } makeExample(); timer = setInterval(() => { if (remainTime > 0) { remainTime--; passedTime++; timeCount.textContent = remainTime; } if (remainTime 'timer'라는 변수를 gameStart() 함수 외부에 생성하고(다른 함수에서도 사용하기 위해) 함수 내부에서 setInterval() 메서드를 할당한다.setInterval(func, delay) 메서드는 실행할 함수(func)와 지연 시간(delay)을 인자로 받는다.setInterval() 메서드는 setTimeout() 메서드의 반환 값처럼 'intervalID'을 반환하고, 이를 이용해 생성한 interval을 취소할 수 있다.1초마다 화면에 보여주는 시간 값을 줄여서 보여주고, 남은 시간(remainTime)이 0이 되면 finishGame() 함수에서 clearInterval(intervalID) 메서드를 호출한다.예제는 textarea에 focus 이벤트가 발생하면 gameStart() 함수를 호출하고 있어서 이미 타이머가 생성된 이후라면 하위 코드가 실행되지 않도록 조건을 만들었다. split() 메서드를 이용해 문자열 관리(는 위험하다)(정리글을 쓰면서 알아보다가 발견...)function makeExample() { exampleContainer.replaceChildren(); // String.split('') better than [...String] or Array.from(String) // better than grapheme-splitter library Array.from(examples[exampleIndex]).forEach((char) => { const span = document.createElement('span'); span.textContent = char; exampleContainer.appendChild(span); }); exampleIndex++; if (exampleIndex >= examples.length) { exampleIndex = 0; } }split(separator, limit) 메서드는 구분자(separator)와 문자열 최대 개수(limit)을 인자로 받는다.구분자는 문자열에서 일치하는 부분을 기준으로 나누는데, 빈 문자열('')을 할당하면 공백이므로 각 문자로 나눈다.처음엔 문자열의 각각 문자를 배열의 요소처럼 다루기 위해서 split() 메서드를 이용했다.⚠ split('')은 문자 단위로 나누지 않는다!하지만 이 split() 메서드에서 공백을 구분자로 하는 게 큰 위험이 있다는 걸 MDN 문서를 통해 확인했다.console.log('𝟘𝟙𝟚𝟛'.split('')); // ["�","�","�","�","�","�","�","�"] console.log('😎😜🙃'.split('')); // ["�", "�", "�", "�", "�", "�"] console.log('अनुच्छेद'.split('')); // ["अ", "न", "ु", "च", "्", "छ", "े", "द"] console.log('Z͑ͫ̓ͪ̂ͫ̽͏̴̙̤̞͉͚̯̞̠͍A̴̵̜̰͔ͫ͗͢L̠ͨͧͩ͘G̴̻͈͍͔̹̑͗̎̅͛́Ǫ̵̹̻̝̳͂̌̌͘!͖̬̰̙̗̿̋ͥͥ̂ͣ̐́́͜͞'.split('')); // ["Z","͑", "ͫ", "̓", ... ]이런 현상이 발생하는 이유는 split() 메서드의 구분자가 빈 문자열인 경우, 전체 문자열을 UTF-16으로 인코딩하기 때문이라고 한다.위와 같은 특수문자는 UTF-16로 표현할 수 없어서 2바이트 인코딩의 값을 쌍으로 묶어 표현했고, 이를 '써로게이트 페어'라고 부른다.즉 split('')은 이 써로게이트 페어가 망가지면서 원하는 결과를 얻지 못하게 된다.이런 다양한 특수문자와 흰두어 같은 다른 나라의 언어를 제대로 나누기 위한 'grapheme-splitter' 라이브러리도 있다.소스를 보면 생각보다 고려해야 하는 게 많아서 엄청난 코드 양을 볼 수 있다.예제는 비교적 간단하고 미리 정해진 문자열을 다루고 있기에 Array.from() 메서드로 변경했다. 회고사용자가 입력한 문자와 문제의 문자를 비교해서 CSS와 스크립트를 작성하는 게 중요한 과제였다.처음엔 늘 하던대로 String.split('')으로 문자열을 나누고 관리했지만 글을 작성하기 위해 알아보다가 이 방식이 문제가 있다는 걸 알게 됐다.MDN 문서를 보면서 문서화가 정말 중요하다는 걸 느끼기도 했고, Stack Overflow 같은 커뮤니티에 문제를 공유하고 함께 고민하는 게 개발 문화의 큰 힘이자 감사한 일이라 생각했다. (감사... 또 감사...)이런 다양한 경우의 수 때문에 테스트 코드를 중요하게 여기고 작성하는 게 아닐까!?훌륭한 개발자의 머리 속에는 if가 가득할 것 같다. 🙄
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
FE
・
미션
・
과제
・
발자국
2024. 03. 02.
0
[인프런 워밍업 클럽 FE 0기] 미션5 - 비밀번호 생성 앱
🔐 Password Genrator GitHub 🔐 Password Genrator DemoRecord by ScreenToGif 개요인프런 워밍업 클럽 FE 0기의 다섯 번째 미션인 '비밀번호 생성 앱' 입니다.따라하며 배우는 자바스크립트 섹션 7~8(Iterator, Generator, Design Pattern) 목표Array.from, fromCharCode() 메서드를 이용해 숫자, 소문자, 대문자 배열 생성동적 변수를 이용해 DOM 요소 조작 및 비밀번호 생성하기 구현Array.from, fromCharCode() 메서드를 이용해 숫자, 소문자, 대문자 배열 생성// index를 이용한 0~9 배열 [0, 1, 2, ...] const numbersArray = Array.from({ length: 10 }, (_, index) => index); // 유니코드를 이용한 소문자 배열 [a, b, c, ...] const smallLettersArray = Array.from({ length: 26 }, (_, index) => String.fromCharCode(97 + index) ); // 유니코드를 이용한 대문자 배열 [A, B, C, ...] const capitalLettersArray = Array.from({ length: 26 }, (_, index) => String.fromCharCode(65 + index) ); const symbolsArray = ['@', '!', '#', '$', '%'];Array.from(arrayLike, mapFn, thisArg) 메서드는 세 개의 인자를 받는다.첫 번째 인자는 순회가 가능한 유사 배열 객체(arrayLike), 두 번째 인자는 배열의 각 요소에 호출할 함수(mapFn), 세 번째 인자로 mapFn 함수 실행 시에 사용할 this(thisArg) 값을 받는다.여기서 유사 배열 객체의 속성인 'length'를 이용해 원하는 길이를 지정하고 두 번째 함수에서 인덱스를 이용해 각 요소의 값을 인덱스로 지정하면 0부터 length-1의 값을 갖는 배열을 만들 수 있다.String.fromCharCode(num1[, ...[, numN]]) 메서드는 UTF-16 코드 유닛의 시퀀스로부터 문자열을 생성해 반환한다.알파벳은 총 25개이며, 알파벳 소문자의 유니코드는 97부터 122까지, 대문자는 65부터 90까지이므로 길이와 index를 각각 알맞게 설정해주면 된다. 동적 변수를 이용해 DOM 요소 조작 및 비밀번호 생성하기function isChecked() { const checkboxes = form.querySelectorAll('input[type="checkbox"]'); let anyCheck = false; checkboxes.forEach((checkbox) => { if (checkbox.checked) { anyCheck = true; } }); return anyCheck; } 요소 하위의 'checkbox' 타입인 요소를 모두 선택하고, 'checked' 속성을 확인해 하나라도 true라면 'anyCheck'라는 동적 변수에 true를 할당한다.form.addEventListener('click', (e) => { isChecked() ? passwordLength.removeAttribute('disabled') : passwordLength.setAttribute('disabled', ''); });이렇게 isChecked() 함수를 실행해서 체크 여부를 확인하고 입력 요소의 속성을 변경한다.const resultArray = []; let password = ''; let requiredNumbers = false; // ... if (checkNumbers.checked) { resultArray.push(...numbersArray); requiredNumbers = true; } // ...생성할 비밀번호에서 반드시 포함해야 하는 속성을 확인하는 required 변수를 만들고 'input[type="checkbox"]' 요소의 각 checked 값을 확인해 true로 변경한다.do { for (let i = 0; i numbersArray.includes(Number(char)))) || (requiredSmallLetters && !password.split('').some((char) => smallLettersArray.includes(char))) || (requiredCapitalLetters && !password.split('').some((char) => capitalLettersArray.includes(char))) || (requiredSymbols && !password.split('').some((char) => symbolsArray.includes(char))) // 정규 표현식으로 검사 // (requiredNumbers && !/[0-9]/.test(password)) || // (requiredSmallLetters && !/[a-z]/.test(password)) || // (requiredCapitalLetters && !/[A-Z]/.test(password)) || // (requiredSymbols && !/[!@#$%]/.test(password)) );for문을 이용해 사용자가 입력한 비밀번호의 자릿수(passwordLength.value)만큼 비밀번호를 생성한다.생성한 비밀번호를 split() 메서드를 이용해 문자마다 나눈 뒤 배열로 만들고, 문자를 하나하나 기존에 생성한 배열의 요소와 비교한다.만약 생성한 비밀번호에 반드시 포함해야 하는 속성이 없다면(required가 true인데 다음 조건식을 만족하지 않는 경우) 다시 생성한다.이를 좀 더 쉽게 검사하기 위한 정규 표현식도 있지만, 모른다는 가정 하에(잘 모르기도 했지만 😂) 구현을 해보려 했다. 회고다양한 플랫폼에서 회원가입과 로그인은 필수적인 사항일 텐데, 여기서 사용하는 검사식은 플랫폼만큼이나 다양하다고 들었다.왜냐하면 이 부분은 보안과 연결되어 있어서 꽤 민감하기 때문이다.코드를 작성하다 보니 비밀번호를 무작위로 생성하는 것보다 조건을 만들고 통과시키는 게 더 어려웠다.특히 input의 checked 여부에 따라 결과에 반드시 포함시켜야 한다고 작성하는 부분이 꽤 오래 걸렸다.처음엔 전체가 아니라 하나하나 만들 때마다 검사했지만, while의 조건에서 OR( || ) 연산자로 검사하다 보니 무한 루프에 빠져버렸다.결국 전체를 비교하고 다시 만드는 코드로 변경했지만 뭔가 좋은 방법이 아닌 것 같아서 찜찜하다. 🤔물론 보통은 생성이 아니라 사용자가 입력한 값을 비교하겠지만...여러 변수와 다양한 조건을 고려해서 효율적인 코드를 작성할 수 있는 개발자가 되고 싶다!
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
FE
・
미션
・
과제
・
발자국
2024. 02. 29.
1
[인프런 워밍업 클럽 FE 0기] 미션4-2 - GitHubFinder 앱
🔍 github-finder-app GitHub 🔍 github-finder-app 개요인프런 워밍업 클럽 FE 0기의 네 번째 미션인 'GitHubFinder 앱' 입니다.따라하며 배우는 자바스크립트 섹션 5(OOP), 섹션 6(비동기) 목표Fetch API 를 이용해 깃허브 유저 목록 불러오기Closure 를 이용해 Debounce Function 만들기 구현Fetch API 를 이용해 깃허브 유저 목록 불러오기async function loadUser(input) { prevInputValue = input; try { // const response = await fetch('./src/javascript/user.json'); const response = await fetch(`${url}/${input}`); if (!response.ok) { throw new Error('Failed to fetch user json'); } const json = await response.json(); setUserAvatar(json); setUserInfo(json); await loadUserRepos(json); } catch (error) { console.error(error); } }fetch() 메서드의 응답은 HTTP 응답 전체를 나타내는 'response' 객체를 반환한다.response의 ok 속성은 응답의 성공 여부를 불리언 값으로 가지고 있다.따라서 응답이 성공이 아닐 경우 오류 객체(new Error())를 반환하고 catch 문으로 Promise의 오류를 처리한다.응답에 성공한 response 객체를 JSON으로 사용하기 위해선 json() 메서드를 이용해 파싱해야 한다. Closure 를 이용해 Debounce Function 만들기// debounce debounceInput.addEventListener('input', debounce(loadUser, 1000)); // debounceInput.addEventListener('input', e => callback(e)); function debounce(callback, delay = 0) { // timer는 부모 함수에서 선언된 지역 변수 let timer = null; return (arg) => { // 여기서 arg는 input event if (timer) { // 이미 타이머가 있는데 또 실행되면 타이머 삭제 clearTimeout(timer); } // 변수 timer는 부모 함수에서 선언되었지만 내부 함수에서 사용(클로저) timer = setTimeout(() => { callback(arg.target.value); }, delay); }; } 요소의 'input' 이벤트는 요소의 value가 변경될 때마다 발생한다.만약 사용자가 입력할 때마다 서버에 데이터를 요청한다면 서버의 부하가 커지기 때문에 좋은 방법은 아니다.이럴 때 사용자의 입력이 끝난 뒤 마지막 value를 이용해 서버로 요청하는 게 효율적인 방법이라 할 수 있다.함수의 실행 요청이 반복될 때 마지막 요청만으로 실행하는 걸 '디바운싱(debouncing)'이라고 부른다.debounce 함수는 인자로 실행할 함수를 받고 자식 함수를 반환한다.부모 함수인 debounce 함수에서 선언한 변수(timer)를 자식 함수에서 사용할 수 있는 클로저(Closure)를 이용해 자식 함수의 setTimeout() 메서드의 반환 값인 'timeoutID'를 할당한다.변수 'timer'에 할당한 timeoutID를 이용해 setTimeout() 메서드의 지연 시간(delay)이 종료되기 전에 요청이 들어왔다면 이전에 생성한 타이머를 clearTimeout() 메서드를 이용해 종료하고 다시 타이머를 할당한다. 이렇게 delay로 설정한 시간 이내에 사용자의 입력이 없을 경우 API 요청 함수를 실행하게 된다. 반복적인 함수의 실행을 다루는 방법으로 디바운싱(debouncing)와 쓰로틀링(throttling)이 있다.여러 변수를 고려해 'lodash' 라이브러리의 debounce를 많이 사용한다. 회고이번 미션은 debounce가 반환하는 자식 함수의 인자(argument)가 어떤 타입인지 알기 때문에 callback 함수에 전달하는 인자를 수정해서 미숙한 debounce 함수라고 볼 수 있다.늘 라이브러리를 통해 사용하던 함수를 만들려고 하니 모르는 것도 많고, 고려해야 할 부분이 많다는 걸 알게 됐다.자바스크립트의 기초를 잘 알아야 이런 라이브러리 메서드의 원리를 이해하기도 쉽고, 커스텀하기에 수월한 것 같다.(외의로 GitHub의 API 요청이 API key 없이도 되어서 신기했고, 그 덕에 조금은 수월했다. 아주 조금... 😵) DemoRecord by ScreenToGif
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
FE
・
미션
・
과제
・
발자국
2024. 02. 27.
0
[인프런 워밍업 클럽 FE 0기] 1주차 발자국
미션1 - 음식 메뉴 앱깃허브 : 🍝food-recipe-app블로그 : https://www.inflearn.com/blogs/6660미션2 - 가위 바위 보 앱깃허브 : 👊Rock-🖐Paper-✌Scissors-app블로그 : https://www.inflearn.com/blogs/6742미션3 - 퀴즈 앱깃허브 : 🤔 quiz-app블로그 : https://www.inflearn.com/blogs/6860미션4-1 - 책 리스트 앱깃허브 : 📚 book-list-app블로그 : https://www.inflearn.com/blogs/6877
프론트엔드
・
워밍업
・
워밍업클럽
・
프론트엔드
・
프론트
・
미션
・
과제
・
발자국
・
FE