문제: 프로그래밍 언어 검색
index.html
이런 형태의 화면을 구성해야한다 index.html을 참고하여 javascript를 컴포넌트로 분리하고 각 태그들을 javascript를 사용하여 구현해야함
<html>
<head>
<title>2022 FE 데브매칭</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<main class="App">
<div class="SelectedLanguage">
<ul>
<li>JavaScript</li>
<li>Python</li>
<li>Elixir</li>
<li>Java</li>
<li>PHP</li>
</ul>
</div>
<form class="SearchInput">
<input class="SearchInput__input" type="text" placeholder="프로그램 언어를 입력하세요." value="Script">
</form>
<div class="Suggestion">
<ul>
<li class="Suggestion__item--selected">
Action<span class="Suggestion__item--matched">Script</span></li>
<li>Java<span class="Suggestion__item--matched">Script</span></li>
<li>Type<span class="Suggestion__item--matched">Script</span></li>
<li>Pure<span class="Suggestion__item--matched">Script</span></li>
</ul>
</div>
</main>
<script src="components/index.js" type="module"></script>
</body>
</html>
1. 초기 파일 만들기
Id값이 App인 div태그 하나를 만들어서 그 안에 각 컴포넌트들을 조립해보자
그 전에 javascript로 잘 적용 되는지 초기 파일을 만들어보자
index.html
<html>
<head>
<title>2022 FE 데브매칭</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<main class="App">
</main>
<script src="components/index.js" type="module"></script>
</body>
</html>
기존에 있던 index.html에 main태그 빼고 다 지움 그리고 index.js 불러오기함
components폴더 생성
index.js
import App from "./App.js";
new App({ target: document.querySelector('.App') })
class 명이 App인 main태그를 선택하고 App.js 파일을 만들어서 props로 전달해줄거다
components폴더
App.js
export default function App({target}) { //구조분해할당
this.render = () => {
target.innerHTML = `
<h1>안녕하세요</h1>
`;
}
this.render(); //render 호출
}
2. SearchInput.js 컴포넌트 구현하기
SearchInput.js 생성
export default function SearchInput({target, initialState}){
this.state = initialState;
//폼태그 생성
this.element = document.createElement('form');
//class명 생성
this.element.className = "searchForm";
//app 요소에 form태크 추가하기
target.appendChild(this.element);
this.render = () => {
this.element.innerHTML = `
<input type="text" value="${this.state}" placeholder="검색할 언어를 입력하세요"/>
`
}
this.render(); // render()호출
}
App.js
app에 SearchInput 조립하기
import SearchInput from "./SearchInput.js"
export default function App ({target}){
/* this.render = () => {
target.innerHTML =`
<h1>안녕하세요</h1>
`
console.log("aaaaa")
}
this.render()*/ //테스트로 만든건 지워주기!!!!
// SearchInput 조립
const searchInput = new SearchInput({ //target과 initialState를 props로 전달
target,
initialState: ''
})
}
api폴더 생성
api.js생성
서버에 데이터 요청
export const END_FATCH_POINT = 'http://localhost:3006' // 내가 만든 서버 주소
const request = async (url) => {
//url경로로 fetch전송
const res = await fetch(url);
if(res) { // 데이터를 받아올경우 json형태로 변환해서 리턴
const json = await res.json()
return json
}
throw new Error('문제가 생겼습니다')
}
export const fetchList = async (keyword) =>
request(`${END_FATCH_POINT}/languages?keyword=${keyword}`)
App.js
onChange를 props로 전달
import { fetchList } from "../api/api.js"
import SearchInput from "./SearchInput.js"
export default function App ({target}){
// SearchInput 조립
const searchInput = new SearchInput({ //target과 initialState를 props로 전달
target,
initialState: '',
//onChange함수 생성
onChange: async (keyword) => { //SearchInput 에서 input에 value 값을 받으면 여기에서
// 서버로 키워드에 맞게 데이터 요청함 그걸 콘솔에 출력
const language = await fetchList(keyword)
console.log(language)
}
})
}
SearchInput.js
keyup이벤트 사용(키를 눌렀을때 props로 받은 onChange 실행)
export default function SearchInput({target, initialState, onChange}){
this.state = initialState;
//폼태그 생성
this.element = document.createElement('form');
//class명 생성
this.element.className = "searchForm";
//app 요소에 form태크 추가하기
target.appendChild(this.element);
this.element.addEventListener('keyup', (e)=> { //키를 눌렀을때 작동하는 이벤트
onChange(e.target.value)
})
this.render = () => {
this.element.innerHTML = `
<input type="text" value="${this.state}" placeholder="검색할 언어를 입력하세요"/>
`
}
this.render(); // render()호출
}
콘솔에 잘 출력된다
App.js
출력받은 데이터를 상태업데이트 시켜주자
일단 상태관리할 코드 작성 후 상태 업데이트 시켜주자!!
import { fetchList } from "../api/api.js"
import SearchInput from "./SearchInput.js"
export default function App ({target}){
//app에서 관리할 상태값
this.state = {
fetchedlist: []
}
//상태값 업데이트
this.setState = (newState) => {
this.state = {
...this.state,
...newState
}
}
// SearchInput 조립
const searchInput = new SearchInput({ //target과 initialState를 props로 전달
target,
initialState: '',
//onChange함수 생성
onChange: async (keyword) => {
if(keyword.length > 0){
//SearchInput에서 받아온 input value값 길이가 0 이상이면 아래 실행
const language = await fetchList(keyword)
this.setState({ //받아온 데이터를 상태 업데이트
fetchedlist: language
})
console.log(this.state.fetchedlist)
} else { // input에 value값 길이가 0 이하면 빈배열 출력력
this.setState({
fetchedlist: []
})
}
}
})
}
3. Suggestion.js 컴포넌트 구현하기
input에 입력했을 때 그 키워드와 관련된 언어리스트 input 아래에 출력
components폴더
Suggestion.js
export default function Suggestion({target, initialState}) {
this.element = document.createElement('div') //div 태그 생성
this.element.className = 'suggestion'
target.appendChild(this.element) //app 에 추가
this.state = { //Suggestion 상태관리
fetchlist: initialState,
selectedIndex: 0
}
this.setState = (nextState) => { //상태 업데이트
this.state = {
...this.state,
...nextState
}
this.render(); //업데이트 후 render 호출
}
this.render = () =>{
const {fetchlist = [], selectedIndex} = this.state; //상태 구조분해
if(fetchlist.length > 0) { //fetchlist 길이가 0이상이면 생성
this.element.style.display = 'block';
this.element.innerHTML =`
<ul>
${fetchlist.map((list,index)=> `<li data-index="${index}">${list}</li>`).join("")}
</ul>
`;
}else { //fetchlist 길이가 0이하
console.log(111)
this.element.style.display = 'none'
this.element.innerHTML = ``;
}
}
this.render() //render 호출
}
App.js
Suggestion 조립 / suggestion 상태 업데이트 추가
import { fetchList } from "../api/api.js"
import SearchInput from "./SearchInput.js"
import Suggestion from "./Suggestion.js"
export default function App ({target}){
//app에서 관리할 상태값
this.state = {
fetchedlist: []
}
//상태값 업데이트
this.setState = (newState) => {
this.state = {
...this.state,
...newState
}
//상태값이 업데이트 되면서 Suggestion도 업데이트
suggestion.setState({fetchlist: this.state.fetchedlist})
}
// SearchInput 조립
const searchInput = new SearchInput({ //target과 initialState를 props로 전달
target,
initialState: '',
//onChange함수 생성
onChange: async (keyword) => {
if(keyword.length > 0){
//SearchInput에서 받아온 input value값 길이가 0 이상이면 아래 실행
const language = await fetchList(keyword)
this.setState({ //받아온 데이터를 상태 업데이트
fetchedlist: language
})
console.log(this.state.fetchedlist)
} else { // input에 value값 길이가 0 이하면 빈배열 출력력
this.setState({
fetchedlist: []
})
}
}
})
//Suggestion 조립
const suggestion = new Suggestion({ //target과 initialState를 props로 전달
target,
initialState: []
})
}
Suggestion.js
li에 class명 추가, 방향키로 키워드 관련 리스트 선택
export default function Suggestion({target, initialState}) {
this.element = document.createElement('div') //div 태그 생성
this.element.className = 'suggestion'
target.appendChild(this.element) //app 에 추가
this.state = { //Suggestion 상태관리
fetchlist: initialState,
selectedIndex: 0
}
this.setState = (nextState) => { //상태 업데이트
this.state = {
...this.state,
...nextState
}
this.render(); //업데이트 후 render 호출
}
this.render = () =>{
const {fetchlist = [], selectedIndex} = this.state; //상태 구조분해
if(fetchlist.length > 0) { //fetchlist 길이가 0이상이면 생성
this.element.style.display = 'block';
this.element.innerHTML =`
<ul>
${fetchlist.map((list,index)=>`
<li data-index="${index}" class="${index === selectedIndex ?
'Suggestion__item--selected' : ''}">${list}</li>`).join("")}
</ul>
`;
}else { //fetchlist 길이가 0이하
console.log(111)
this.element.style.display = 'none'
this.element.innerHTML = ``;
}
}
this.render() //render 호출
//방향키 up, down이벤트 연결하기
//방향키 위를 누르면 index는 현재에서 -1
//방향키 아래르 누르면 index는 현재에서 1
window.addEventListener('keyup', (e)=>{
const {fetchlist = [], selectedIndex} = this.state;
const inclusionKeys = ['ArrowUp', 'ArrowDown'];
let nextIndex = selectedIndex;
const lastIndex = fetchlist.length -1;
if(inclusionKeys.includes(e.key)) {
if(e.key === 'ArrowUp') { //위 방향키일때
nextIndex = nextIndex === 0 ? lastIndex : nextIndex -1;
}else {//아래 방향키일 때 nextIndex 값이 lastIndex 하고 같은면 0초기화
nextIndex = nextIndex === lastIndex ? 0 : nextIndex + 1;
}
this.setState({ //업데이트
selectedIndex: nextIndex
})
//엔터키를 눌렀을때 해당 키워드가 알람으로 뜸 빈배열일경우 undefined
}else if (e.key === 'Enter') {
alert(fetchlist[this.state.selectedIndex])
}
})
}
SearchInput.js 수정
정해놓은 배열안에 있는 키를 제외한 키를 눌렀을 때 이벤트 추가
submit이벤트 제거 추가
export default function SearchInput({target, initialState, onChange}){
this.state = initialState;
//폼태그 생성
this.element = document.createElement('form');
//class명 생성
this.element.className = "searchForm";
//app 요소에 form태크 추가하기
target.appendChild(this.element);
this.element.addEventListener('keyup', (e)=> { //키를 눌렀을때 작동하는 이벤트
//해당 배열 제외한 키를 눌렀을때 이벤트 발생
const excepList = ['Enter', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']
if(!excepList.includes(e.key)){
onChange(e.target.value);
}
})
//submit이벤트 제거
this.element.addEventListener('submit', (e)=>{
e.preventDefault();
})
this.render = () => {
this.element.innerHTML = `
<input type="text" value="${this.state}" placeholder="검색할 언어를 입력하세요"/>
`
}
this.render(); // render()호출
}
4. SelectLists.js 컴포넌트 구현하기
키워드 선택 후 엔터 눌렀을때 input위에 키워드 추가하기
components폴더
SelectLists.js
export default function SelectLists({target, initialState}) {
this.element = document.createElement('div'); //div 태그 생성
this.element.className ="selectlists"
target.appendChild(this.element); // app에 div태그 추가
this.state = { //SelectLists 상태관리
selects : initialState
}
this.setState = (newState) => { //업데이트
console.log(1123123)
this.state = {
selects: newState
};
console.log(this.state)
this.render(); //업데이트 후 render 호출
}
this.render = () => {
const {selects = [] } = this.state; //상태 구조분해
console.log(selects)
this.element.innerHTML = `
<ul>
${selects.map(list=>`<li>${list}</li>`).join('')}
</ul>
`;//
}
this.render() // 호출
}
App.js
selectLists 조립, 상태관리 selectedlist 추가 ,Suggestion 프로퍼티 추가(onSelect) 후 업데이트
import { fetchList } from "../api/api.js"
import SearchInput from "./SearchInput.js"
import SelectLists from "./SelectLists.js"
import Suggestion from "./Suggestion.js"
export default function App ({target}){
//app에서 관리할 상태값
this.state = {
selectedlist: [],
fetchedlist: []
}
//상태값 업데이트
this.setState = (newState) => {
this.state = {
...this.state,
...newState
}
//상태값이 업데이트 되면서 Suggestion도 업데이트
suggestion.setState({fetchlist: this.state.fetchedlist})
selectLists.setState(this.state.selectedlist)
}
//SelectLists 조립 참고 index.html파일 보면 위치 확인 가능 위치에 맞게 조립할것
const selectLists = new SelectLists({
target,
initialState: []
})
// SearchInput 조립
const searchInput = new SearchInput({ //target과 initialState를 props로 전달
target,
initialState: '',
//onChange함수 생성
onChange: async (keyword) => {
if(keyword.length > 0){
//SearchInput에서 받아온 input value값 길이가 0 이상이면 아래 실행
const language = await fetchList(keyword)
this.setState({ //받아온 데이터를 상태 업데이트
fetchedlist: language
})
console.log(this.state.fetchedlist)
} else { // input에 value값 길이가 0 이하면 빈배열 출력력
this.setState({
fetchedlist: []
})
}
}
})
//Suggestion 조립
const suggestion = new Suggestion({ //target과 initialState를 props로 전달
target,
initialState: [],
onSelect: (list) => {
console.log(list)
const {selectedlist} = this.state;
console.log(selectedlist)
const index = selectedlist.findIndex(selist=>selist === list)
//참이면 해당 인덱스 반환 거짓이면 -1반환
//중복된 값 제거(기존배열에 있던거 제거함)
console.log(index)
if(index > -1) {
console.log("중복제거")
selectedlist.splice(index,1)
}
selectedlist.push(list) // 배열에 추가해주기
console.log(selectedlist)
//배열 갯수 5개로 제한해주기
if(selectedlist.length > 5) {
const startPosition = selectedlist.length -5
console.log(startPosition)
// selectedlist가 6일경우 startPosition = 1
const nselectedlist = selectedlist.slice(startPosition, 5 + startPosition)
// selectedlist.slice(1 , 6) 1~5째까지 자르기 0번째꺼는 제거됨
this.setState({selectedlist: nselectedlist}) // 업데이트
}else{ //5이하일경우 기존 배열 그대로
this.setState({selectedlist: selectedlist})
}
}
})
}
Suggestion.js
onSelect props 추가, 맨 밑에 엔터를 눌렀을때 onSelect 매개변수 전송
export default function Suggestion({target, initialState, onSelect}) {
this.element = document.createElement('div') //div 태그 생성
this.element.className = 'suggestion'
target.appendChild(this.element) //app 에 추가
this.state = { //Suggestion 상태관리
fetchlist: initialState,
selectedIndex: 0
}
this.setState = (nextState) => { //상태 업데이트
this.state = {
...this.state,
...nextState
}
this.render(); //업데이트 후 render 호출
}
this.render = () =>{
const {fetchlist = [], selectedIndex} = this.state; //상태 구조분해
if(fetchlist.length > 0) { //fetchlist 길이가 0이상이면 생성
this.element.style.display = 'block';
this.element.innerHTML =`
<ul>
${fetchlist.map((list,index)=>`
<li data-index="${index}" class="${index === selectedIndex ?
'Suggestion__item--selected' : ''}">${list}</li>`).join("")}
</ul>
`;
}else { //fetchlist 길이가 0이하
console.log(111)
this.element.style.display = 'none'
this.element.innerHTML = ``;
}
}
this.render() //render 호출
//방향키 up, down이벤트 연결하기
//방향키 위를 누르면 index는 현재에서 -1
//방향키 아래르 누르면 index는 현재에서 1
window.addEventListener('keyup', (e)=>{
const {fetchlist = [], selectedIndex} = this.state;
const inclusionKeys = ['ArrowUp', 'ArrowDown'];
let nextIndex = selectedIndex;
const lastIndex = fetchlist.length -1;
if(inclusionKeys.includes(e.key)) {
if(e.key === 'ArrowUp') { //위 방향키일때
nextIndex = nextIndex === 0 ? lastIndex : nextIndex -1;
}else {//아래 방향키일 때 nextIndex 값이 lastIndex 하고 같은면 0초기화
nextIndex = nextIndex === lastIndex ? 0 : nextIndex + 1;
}
this.setState({ //업데이트
selectedIndex: nextIndex
})
//엔터키를 눌렀을때 해당 키워드가 input위에 등록됨
}else if (e.key === 'Enter') {
alert(fetchlist[this.state.selectedIndex])
onSelect(fetchlist[this.state.selectedIndex])
}
})
}
style.css
* { margin: 0; padding:0; box-sizing: border-box;}
li {
list-style: none;
}
body {
font-size: 14px;
line-height: 1.5;
}
#App {
width: 100%;
max-width: 400px;
margin: 80px auto;
padding: 50px;
}
#App > div {
margin: 10px 0;
}
.Suggestion__item--selected {
background-color: aquamarine;
}
.searchForm input {
width: 100%;
line-height: 30px;
border-radius: 15px;
outline: none;
border: 1px solid #ccc;
padding: 0 20px;
}
.suggestion {
border-radius: 15px;
border: 1px solid #ccc;
padding: 10px;
}
.suggestion li {
border-radius: 8px;
padding: 6px;
}
.selectlists ul {
display: flex;
font-size: 12px;
text-align: center;
line-height: 26px;
justify-content: center;
}
.selectlists ul li {
padding: 0 6px;
border-radius:6px;
margin: 0 4px;
border: 1px solid #ccc;
}
끝!!!!!!!!!!!!!!!!!!!!!!!!!
'프로그래머스' 카테고리의 다른 글
[프로그래머스]커스텀 라우터 (0) | 2023.03.04 |
---|
댓글