-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.html
275 lines (233 loc) · 14.9 KB
/
README.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h3 class="modal-title">문제 보기</h3>
</div>
<div class="modal-body">
<div class="markdown github"><h2>주제</h2>
<p>당신은 2020년 상반기에 고양이 사진 검색 사이트를 만들었습니다만, 관리를 잘못하여 사이트가 폐쇄되었습니다.<br>
당신은 지난 실수를 발판 삼아 하반기에는 더 좋은 사이트를 만들려고 하는데요. 이번에는 단순히 사진만을 보여주기보단, 사용성을 개선해 유저가 검색을 쉽게 할 수 있도록 검색어 <strong>추천
기능</strong>을 넣으려고 합니다. 그리고 가능하다면, 성능을 고려하여 여러 가지 부차적인 일도 하고 싶네요. 3시간 내에 사이트를 만들 수 있을까요? 만들고 싶은 결과물은 다음과
같습니다.</p>
<p><img
src="https://grepp-programmers.s3.ap-northeast-2.amazonaws.com/files/production/fec49296-7358-4538-8689-3f448ee800e2/keywords.png"
title="" alt="keywords.png"></p>
<h2>과제 설명</h2>
<ul>
<li>기본적인 화면을 구성하는 베이스 코드가 포함되어 있습니다. 이 베이스 코드를 활용해, ES6+ 문법을 이용해 작성해주세요.</li>
<li>화면을 구성하는 요소를 컴포넌트로 만들어서 코딩합니다.
<ul>
<li>컴포넌트는 function 문법, class 문법 어느 쪽을 사용해도 괜찮습니다.</li>
<li>각 컴포넌트는 각각의 상태 값을 가지고 있고, render 함수를 통해 현재 상태를 렌더링 합니다.</li>
<li>render 함수는 파라미터를 넘겨받는 것 없이 해당 컴포넌트의 상태 변수들만 갖고 렌더링이 일어나도록 설계해야 합니다.</li>
<li>DOM 접근은 최소화해야 합니다.</li>
<li>컴포넌트 간의 결합은 최대한 느슨해야 합니다.</li>
</ul>
</li>
<li>과제는 chrome, edge, firefox, safari 등 모던 브라우저 최신 버전에서 동작하는 것을 원칙으로 합니다.</li>
</ul>
<h2>수행 기술</h2>
<ul>
<li>JavaScript(ES6+)</li>
<li>설치되어있는 모듈(node_modules) 외에 다른 외부 라이브러리는 사용하지 않도록 합니다. 예를 들어 jQuery, Webpack, Lodash, Axios, Angular,
React, Vue, Immutable-js, Ramda 등을 사용할 수 없습니다.
</li>
</ul>
<h2>요구사항</h2>
<p><strong>중요</strong> 말머리로 <code>[필수]</code> 가 붙은 요구사항은 반드시 수행해주세요.</p>
<h3>검색어 추천</h3>
<ul>
<li><strong>[필수]</strong> 추천 검색어 API를 이용해 입력란에 추천 검색어를 보여주세요. 추천 검색어는 엔터를 치는 등 별도의 행위가 없을 때에도 자동으로 보여야 합니다.
<ul>
<li><code>keywords</code>라는 class 명으로 스타일링 되어있습니다. 자세한 내용은 index.html과 style.css를 참고하시리 바랍니다.</li>
<li>API에 대한 설명은 하단에 있습니다.</li>
<li>API에서 에러가 나거나, 추천 검색어가 없는 경우에도 검색 기능을 사용하는 데에는 문제가 없어야 합니다.</li>
</ul>
</li>
</ul>
<h3>검색어 추천 - 사용성 개선</h3>
<ul>
<li>키보드와 마우스를 이용해서 추천 검색어를 선택할 수 있게 만들어 주세요.
<ol>
<li>esc를 누르면 추천 검색어 창이 닫힙니다.</li>
<li>키보드의 위, 아래 키를 누르면 추천 검색어 하이라이트가 옮겨지고 엔터를 누르면 하이라이트가 위치한 검색어가 입력창에 반영되고 사진이 검색됩니다.</li>
<li>마우스로 다른 곳을 클릭하여 input이 focus를 잃어버리는 경우 추천 검색어 창이 닫힙니다.</li>
<li>마우스로 추천 검색어를 누르면 커서가 위치한 검색어가 입력창에 반영되며 사진이 검색됩니다.</li>
</ol>
</li>
<li>추천 검색어가 로딩되는 중임을 알리는 UI적 처리를 해주세요.</li>
</ul>
<h3>검색</h3>
<ul>
<li><strong>[필수]</strong> 검색 시 API로 받은 고양이 사진이 화면에 렌더 되어야 합니다.</li>
</ul>
<h3>검색 - 사용성 개선</h3>
<ul>
<li>검색 중 에러가 발생한 경우, 에러가 발생했다는 것을 알리는 UI적 처리를 해주세요.</li>
<li>검색 결과가 로딩되는 중임을 알리는 UI적 처리를 해주세요.</li>
<li>검색이 일어나면 url 뒤에 <code>?q={검색어}</code>를 붙입니다.
<ul>
<li>페이지 url에 검색어가 존재하는 경우 페이지에 진입하자마자 해당 검색어로 검색된 결과가 나오도록 합니다.</li>
</ul>
</li>
</ul>
<h3>퍼포먼스 향상</h3>
<ul>
<li>검색어별 추천 검색어를 로컬에 캐싱해서 사용하도록 합니다.</li>
<li>추천 검색어 API 호출 중 새로운 검색어 입력이 감지되면 기존의 ajax 요청을 취소하고 새로운 검색어를 기준으로 API 요청을 보내주세요.</li>
<li>Debounce를 구현합니다. 이를 통해 검색어가 입력될 때마다 서버에 요청이 일어나지 않게 합니다.</li>
<li>키워드별 검색 결과를 캐싱하여 사용합니다. 단, 캐싱된 데이터는 브라우저를 닫으면 사라져야 합니다.</li>
</ul>
<h3>코드 구조 관련</h3>
<ul>
<li>각 컴포넌트 간의 연동은 가급적 직접 호출하지 말고, App 컴포넌트를 만든 뒤 이 컴포넌트가 콜백 함수를 이용해 조율하는 형태로 만듭니다.</li>
<li>ES6 module 형태로 코드를 변경합니다.
<ul>
<li><code>webpack</code> , <code>parcel</code> 과 같은 번들러를 사용하지 말아 주세요.</li>
<li>해당 코드 실행을 위해서는 <code>http-server</code> 모듈을(로컬 서버를 띄우는 다른 모듈도 사용 가능) 통해 <code>index.html</code> 을 띄워야
합니다.
</li>
<li>터미널에서 <code>npm start</code> 명령어를 이용해 로컬 서버를 띄운 후 작업을 합니다.</li>
</ul>
</li>
<li>API fetch 코드를 <code>async</code> , <code>await</code> 문을 이용하여 수정해주세요. 해당 코드들은 에러가 났을 경우를 대비해서 적절히 처리가
되어있어야 합니다.
<ul>
<li>에러 발생을 알리는 UI적 처리까지 하면 더욱 좋습니다.</li>
<li>서버에서는 아주 가끔 데이터 모양이 이상하게 내려오기도 합니다. 이를 대응해주세요.</li>
</ul>
</li>
<li>API의 status code에 따라 에러 메시지를 분리하여 작성해야 합니다.</li>
<li>컴포넌트 내부의 함수들이나 Util 함수들을 작게 잘 나누어주세요.</li>
</ul>
<hr>
<h2>API 설명</h2>
<h3>1. 추천 검색어 API</h3>
<h5>URL</h5>
<div class="highlight"><pre class="codehilite"><code>GET https://jf3iw5iguk.execute-api.ap-northeast-2.amazonaws.com/dev/api/cats/keywords?q={keyword}
</code></pre>
</div>
<h5>Parameter</h5>
<ul>
<li>q: (필수) 검색어</li>
</ul>
<h5>Response</h5>
<p>다음과 같은 추천 검색어로 구성된 배열</p>
<p><code>["name1", "name2", "name3"]</code></p>
<p><strong>예시</strong></p>
<p>검색창에 <strong>헤어</strong>를 입력하면 아래와 같이 요청합니다.</p>
<div class="highlight"><pre class="codehilite"><code>curl <span class="s1">'https://jf3iw5iguk.execute-api.ap-northeast-2.amazonaws.com/dev/api/cats/keywords?q=%ED%97%A4%EC%96%B4'</span>
// response
<span class="o">[</span>
<span class="s2">"브리티쉬 롱헤어"</span>,
<span class="s2">"브리티쉬 숏헤어"</span>,
<span class="s2">"컬러포인트 숏헤어"</span>,
<span class="s2">"엑조틱 숏헤어"</span>,
<span class="s2">"오리엔탈 숏헤어"</span>
<span class="o">]</span>
</code></pre>
</div>
<h3>2. 검색 API</h3>
<h5>URL</h5>
<div class="highlight"><pre class="codehilite"><code>GET http://jf3iw5iguk.execute-api.ap-northeast-2.amazonaws.com/dev/api/cats/search?q={keyword}
</code></pre>
</div>
<h5>Parameter</h5>
<ul>
<li>q: (필수) 검색어</li>
</ul>
<h5>Response</h5>
<p>다음과 같이 검색결과로 이루어진 배열</p>
<div class="highlight"><pre class="codehilite"><code><span class="p">{</span><span class="w">
</span><span class="s2">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span
class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"id"</span><span class="p">,</span><span class="w">
</span><span class="s2">"url"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"url"</span><span class="p">,</span><span class="w">
</span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"name"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
</div>
<p><strong>예시</strong></p>
<p><code>브리티쉬 롱헤어</code>로 검색했을 경우</p>
<div class="highlight"><pre class="codehilite"><code>curl <span class="s1">'http://jf3iw5iguk.execute-api.ap-northeast-2.amazonaws.com/dev/api/cats/search?q=%EB%B8%8C%EB%A6%AC%ED%8B%B0%EC%89%AC%20%EB%A1%B1%ED%97%A4%EC%96%B4'</span>
</code></pre>
</div>
<p>대략 아래와 같은 응답이 내려옵니다.</p>
<div class="highlight"><pre class="codehilite"><code><span class="p">{</span><span class="w">
</span><span class="s2">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span
class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"MTc5NDU2MQ"</span><span class="p">,</span><span class="w">
</span><span class="s2">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://cdn2.thecatapi.com/images/MTc5NDU2MQ.jpg"</span><span
class="p">,</span><span class="w">
</span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"British Longhair / 브리티쉬 롱헤어"</span><span
class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"5cc"</span><span class="p">,</span><span class="w">
</span><span class="s2">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://cdn2.thecatapi.com/images/5cc.jpg"</span><span
class="p">,</span><span class="w">
</span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"British Longhair / 브리티쉬 롱헤어"</span><span
class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"bbr"</span><span class="p">,</span><span class="w">
</span><span class="s2">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://cdn2.thecatapi.com/images/bbr.jpg"</span><span
class="p">,</span><span class="w">
</span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"British Longhair / 브리티쉬 롱헤어"</span><span
class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"2do"</span><span class="p">,</span><span class="w">
</span><span class="s2">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://cdn2.thecatapi.com/images/2do.jpg"</span><span
class="p">,</span><span class="w">
</span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"British Longhair / 브리티쉬 롱헤어"</span><span
class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"id"</span><span class="p">:</span><span class="w"> </span><span
class="s2">"bna"</span><span class="p">,</span><span class="w">
</span><span class="s2">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://cdn2.thecatapi.com/images/bna.jpg"</span><span
class="p">,</span><span class="w">
</span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"British Longhair / 브리티쉬 롱헤어"</span><span
class="w">
</span><span class="p">},</span><span class="w">
</span><span class="err">...</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">이하</span><span class="w"> </span><span
class="err">줄임</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
</div>
</div>
</div>
<div class="modal-footer">
<a class="btn btn-primary" data-dismiss="modal" href="#">닫기</a>
</div>
</div>
</div>
</body>
</html>