diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/App.js" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/App.js" new file mode 100644 index 0000000..5354a84 --- /dev/null +++ "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/App.js" @@ -0,0 +1,22 @@ +import SearchInput from "./components/SearchInput.js"; +import { fetchLanguages } from "./core/api.js"; + +export default function App({ $target }) { + this.state = { + fetchedLanguages: [], + selectedLanguages: [], + }; + + this.setState = function (nextState) { + // + }; + + const searchInput = new SearchInput({ + $target, + initialState: "", + onChange: async (keyword) => { + const data = await fetchLanguages(keyword); + console.log(data); + }, + }); +} diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/components/SearchInput.js" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/components/SearchInput.js" new file mode 100644 index 0000000..7579e34 --- /dev/null +++ "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/components/SearchInput.js" @@ -0,0 +1,15 @@ +export default function SearchInput({ $target, initialState }) { + this.$element = document.createElement("form"); + this.$element.className = "SearchInput"; + this.state = initialState; + + $target.appendChild(this.$element); + + this.render = () => { + this.$element.innerHTML = ` + + `; + }; + + this.render(); +} diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/core/api.js" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/core/api.js" new file mode 100644 index 0000000..74e4615 --- /dev/null +++ "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/core/api.js" @@ -0,0 +1,15 @@ +export const API_END_POINT = "API END POINT"; + +const request = async (url) => { + const res = await fetch(url); + + if (res.ok) { + const json = await res.json(); + return json; + } + + throw new Error("요청에 실패함"); +}; + +export const fetchLanguages = async (keyword) => + request(`${API_END_POINT}/languages?keyword=${keyword}`); diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/index.html" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/index.html" new file mode 100644 index 0000000..1f7680a --- /dev/null +++ "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/index.html" @@ -0,0 +1,36 @@ + + + 2022 FE 데브매칭 + + + +
+
+ +
+
+ +
+
+ +
+
+ + + + \ No newline at end of file diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/index.js" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/index.js" new file mode 100644 index 0000000..c0ca41c --- /dev/null +++ "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/index.js" @@ -0,0 +1,3 @@ +import App from "./App.js"; + +new App({ $target: document.querySelector(".App") }); diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/j.md" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/j.md" deleted file mode 100644 index e69de29..0000000 diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/main.js" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/main.js" new file mode 100644 index 0000000..2ec78d4 --- /dev/null +++ "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/main.js" @@ -0,0 +1,127 @@ +// 버벅임 ... 서버 키고 실행해야 함 +// 할 때마다 실행? 닫기? +// 방향키, 보너스 제외 1시간 반 + +const $ = (selector) => document.querySelector(selector); + +const resultContainer = $(".Suggestion"); +const resultWrapper = resultContainer.querySelector("ul"); +const resultArr = []; + +let focusingIdx = 0; + +function renderKeyword() { + const renderingWrapper = $(".SelectedLanguage").querySelector("ul"); + + renderingWrapper.innerHTML = ""; + resultArr.map((result) => { + const li = document.createElement("li"); + li.innerText = result; + renderingWrapper.appendChild(li); + }); +} + +function addRenderingKeyword(result) { + window.alert(result); + + if (resultArr.includes(result)) return; + + if (resultArr.length >= 5) resultArr.shift(); + resultArr.push(result); + + renderKeyword(); +} + +function separateResultStr(result, keyword) { + const _result = (result + "").toLowerCase(); + const _keyword = (keyword + "").toLowerCase(); + + const keywordLength = _keyword.length; + const matchingIdx = _result.indexOf(_keyword); + + const str1 = _result.slice(0, matchingIdx); + const str2 = _result.slice(matchingIdx, matchingIdx + keywordLength); + const str3 = _result.slice(matchingIdx + keywordLength); + + return `${str1}${str2}${str3}`; +} + +function focusListWithArrowKeyNEnter(e) { + const maxLength = resultWrapper.childNodes.length; + const prevFocusingIdx = focusingIdx; + + switch (e.key) { + case "Enter": + addRenderingKeyword(resultWrapper.childNodes[focusingIdx].innerText); + return; + case "ArrowUp": + focusingIdx = (focusingIdx - 1) % maxLength; + break; + case "ArrowDown": + focusingIdx = (focusingIdx + 1) % maxLength; + break; + default: + break; + } + + resultWrapper.childNodes[prevFocusingIdx]?.classList.remove( + "Suggestion__item--selected" + ); + resultWrapper.childNodes[focusingIdx]?.classList.add( + "Suggestion__item--selected" + ); +} + +async function handleInputKeyword(keyword, key) { + if (`${key}` === "ArrowUp" || `${key}` === "ArrowDown") return; + + // 함수로 분리하면 Promise 반환? + const response = await fetch( + `https://wr4a6p937i.execute-api.ap-northeast-2.amazonaws.com/dev/languages?keyword=${keyword}`, + { + method: "GET", + } + ); + const resultData = await response.json(); + + if (keyword === "" || resultData.length === 0) { + resultWrapper.innerHTML = ""; + resultContainer.style.display = "none"; + return; + } else { + resultContainer.style.display = "block"; + } + + resultWrapper.innerHTML = ""; + resultData.map((result, idx) => { + const li = document.createElement("li"); + + if (idx === focusingIdx) { + li.setAttribute("class", "Suggestion__item--selected"); + } + + li.innerHTML = separateResultStr(result, keyword); + li.addEventListener("click", () => addRenderingKeyword(result)); + resultWrapper.appendChild(li); + }); +} + +function init() { + const form = $("form.SearchInput"); + form.addEventListener("submit", (e) => e.preventDefault()); + + resultContainer.style.display = "none"; + + const input = $(".SearchInput__input"); + input.addEventListener("keyup", (e) => + handleInputKeyword(e.target.value, e.key) + ); + input.focus(); + + document.addEventListener("keyup", focusListWithArrowKeyNEnter); +} + +init(); + +// useEffect 없이 디바운스? +// API 캐싱? diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/style.css" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/style.css" new file mode 100644 index 0000000..4c3f740 --- /dev/null +++ "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/style.css" @@ -0,0 +1,96 @@ +* { + box-sizing: border-box; +} + +ul { + padding: 0; + margin: 0; +} + +li { + list-style: none; +} + +html, body { + width: 100%; + height: 100%; + margin: 0; +} + +.App { + display: flex; + flex-direction: column; + position: relative; + height: 100%; + justify-content: center; + align-items: center; +} + +.SearchInput { + width: 300px; +} + +.SearchInput__input { + width: 100%; + border: 1px solid black; + border-radius: 24px; + padding: 10px 24px; + font-size: 20px +} + +.Suggestion { + position: absolute; + font-size: 20px; + width: 300px; + top: 50%; + z-index: 1; + padding: 10px; + border-radius: 24px; + border: 1px solid black; + +} + +.Suggestion ul { + width: 100%; +} + +.Suggestion li { + padding: 10px; + cursor: pointer; + width: 100%; +} + +.Suggestion li:hover { + background-color: #90CDF4; +} + +.Suggestion__item--selected { + background-color: #BEE3F8; +} + +.Suggestion__item--matched { + background-color: #9AE6B4; +} + +.SelectedLanguage { + display: flex; + justify-content: center; + margin: 0 auto; + flex-wrap: nowrap; + width: 800px; + height: 37.5px; + margin-top: -100px; + margin-bottom: 10px; + +} + +.SelectedLanguage li { + display: inline-block; + padding: 8px; + border: 1px solid #ccc; + border-radius: 8px; + max-width: 100px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; +} \ No newline at end of file diff --git "a/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211.md" "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211.md" new file mode 100644 index 0000000..e95b301 --- /dev/null +++ "b/\352\265\254\355\230\204 \352\263\274\354\240\234 \355\222\200\354\235\264/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211/\354\243\274\355\225\250/\355\224\204\353\241\234\352\267\270\353\236\230\353\260\215 \354\226\270\354\226\264 \352\262\200\354\203\211.md" @@ -0,0 +1,57 @@ +## 1. 초반에 엄청 버벅임 ... 동작법 익숙해지는 데에 시간을 많이 소요함 + +--- + +## 2. 할 때마다 실행 클릭하고, 하나씩 닫고 ... 결국 고치고 저장하고 새로고침 했는데 이거 맞나? + +--- + +## 3. 차례대로 쭉 풀며 기본 구현사항들 풀어나감, + +괜히 제한시간이 있다고 생각하니까 리팩토링보다는 막 갈기게 됨 + +여유 가지는 연습이 필요할 듯 + +--- + +## 4. 방향키로 리스트 탐색 + 추가 구현사항 제외하고 1시간 반 정도 소요함 + +괜히 시간 많다고 느껴지니까 집중력 매우 떨어짐 + +한 줄 쓰고 인스타 보고 반복,, 쩝 실전 때는 안 그러겠지? + +--- + +## 5. LocalStorage 사용해서 새로고침 상태 유지는 귀찮아서 그냥 넘김, 무슨 심보인지 모르겠음 + +--- + +## 6. 결과적으로 API 캐싱과 디바운스 기능 빼고는 얼추 완성함 + +근데 이거 막 갈겨서 코드리뷰 할 맛 안 나겠다 싶음 ... 빨리 친구들 코드 봐보고 싶음 + +--- + +## 7. 해설지도 대충 봐봤는데 API 캐싱 신기 .! 디바운스 기능은 그냥 라이브러리 쓰는 건가~.~ + +아래는 해설지의 캐싱 기능 + +```javascript +const cache = {}; + +const request = async (url) => { + if (cache[url]) { + return cache[url]; + } + + const res = await fetch(url); + + if (res.ok) { + const json = await res.json(); + cache[url] = json; + return json; + } + + throw new Error("요청에 실패함"); +}; +```