Seoulite - ๐Ÿ—บ ์„œ์šธ์‹œ wifi ์œ„์น˜ ์ง€๋„ ์•ฑ (์นด์นด์˜ค ์ง€๋„ API, ์„œ์šธ ์—ด๋ฆฐ๋ฐ์ดํ„ฐ๊ด‘์žฅ ํ™œ์šฉ)

bbio3oยท2021๋…„ 1์›” 19์ผ
8

โœจ ํ”„๋กœ์ ํŠธ Projects

๋ชฉ๋ก ๋ณด๊ธฐ
1/4
post-thumbnail

๐Ÿ” Project Overview

๋Œ€ํ•™์ƒ ์ฝ”๋”ฉ ์—ฐํ•ฉ ๋™์•„๋ฆฌ '๋ฉ‹์Ÿ์ด ์‚ฌ์ž์ฒ˜๋Ÿผ'์—์„œ ์ง„ํ–‰ํ•œ ํ”„๋กœ์ ํŠธ๋กœ,
์ง€๋„ Api๋ฅผ ์ด์šฉํ•œ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž๋Š” ์ทจ์ง€๋กœ ์‹œ์ž‘ ๋œ ์Šคํ„ฐ๋””๋ชจ์ž„์—์„œ ๋งŒ๋“  ํ”„๋กœ์ ํŠธ ์ž…๋‹ˆ๋‹ค.
๋ฐ–์—์„œ ํ•ธ๋“œํฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋–จ์–ด์ ธ๊ฐ€์„œ ๊ธ‰ํ•˜๊ฒŒ ์™€์ดํŒŒ์ด๊ฐ€ ํ•„์š”ํ• ๋•Œ, ์„œ์šธ์‹œ์˜ ์™€์ดํŒŒ์ด๊ฐ€ ์„ค์น˜๋œ ๊ณณ์˜ ์œ„์น˜ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ,
์ž์‹ ์˜ ํ˜„์žฌ ์œ„์น˜๋ฅผ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ฃผ๋ณ€์˜ ๊ฐ€๊นŒ์šด ์™€์ดํŒŒ์ด ์œ„์น˜๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
์นด์นด์˜ค ์ง€๋„ API์™€ ์„œ์šธ์‹œ ์—ด๋ฆฐ๋ฐ์ดํ„ฐ ๊ด‘์žฅ API๋ฅผ ์ด์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค!

โœจ ์•„๋ž˜ ๋งํฌ์—์„œ ํ”„๋กœ์ ํŠธ ๋ฐ๋ชจ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
Seoulite Demo


์‚ฌ์šฉ๊ธฐ์ˆ 

  • HTML5 / CSS3
  • JavaScript(ES6+)
  • React
  • Styled-components

APIs

๊ตฌํ˜„๊ธฐ๋Šฅ์‚ฌํ•ญ

  • ์œ„์น˜ ์ •๋ณด ํ™•์ธ
  • ์ง€๋„ ํ™•๋Œ€/์ถ•์†Œ
  • ์ง€๋„ ๊ฒ€์ƒ‰
  • ํ˜„์žฌ ์œ„์น˜ ๊ฒ€์ƒ‰(Geolocation)
  • ๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ ์ง€์›
  • ๋ฐ˜์‘ํ˜• ์›น


โœจ ์ฒซ ๋žœ๋”ฉ ํŽ˜์ด์ง€, ์ง€๋„ ํ™•๋Œ€ / ์ถ•์†Œ ๊ธฐ๋Šฅ


ํŒจ์น˜ํ•œ api์˜ ์œ„์น˜์ •๋ณด ์–‘์ด ๋ฐฉ๋Œ€ํ•˜์—ฌ Kakao map api์—์„œ ์ œ๊ณตํ•˜๋Š” MarkerClusterer๋ฅผ ์ด์šฉํ•˜์—ฌ ์œ„์น˜ ๋งˆ์ปค ์ •๋ณด๋ฅผ ํด๋Ÿฌ์Šคํ„ฐํ™” ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
์ง€๋„์˜ ํ™•๋Œ€์™€ ์ถ•์†Œ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ์˜ ์คŒ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ํ™•๋Œ€ ์ถ•์†Œ๋„ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋งˆ์ปค ํด๋Ÿฌ์Šคํ„ฐ๋Ÿฌ ๋„์ž…์„ ๊ฒฐ์‹ฌํ•œ ์ด์œ ๋Š” ํด๋Ÿฌ์Šคํ„ฐํ™” ํ•˜๊ธฐ ์ „์˜ ๋งˆ์ปค ์ •๋ณด๋Š” ๋ฐ์ดํ„ฐ ๋Ÿ‰์ด ๋งŽ์•„ ์ด๋ ‡๊ฒŒ ์•„๋ž˜ ์Šคํฌ๋ฆฐ์ƒท๊ณผ ๊ฐ™์ด ์–ด๋งˆ ๋ฌด์‹œํ•˜๊ฒŒ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค...๐Ÿ’ฉ
[์นด์นด์˜ค ๋งต API ๋งˆ์ปค ํด๋Ÿฌ์Šคํ„ฐ๋Ÿฌ ์‚ฌ์šฉํ•˜๊ธฐ ๋งํฌ]
(https://apis.map.kakao.com/web/sample/basicClusterer/)

<ํด๋Ÿฌ์Šคํ„ฐ๋Ÿฌ ๋„์ž… ์ „ ๋งˆ์ปค๋“ค>


โœจ ์ง€๋„ ์œ„์น˜๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ

์‚ฌ์šฉ์ž๋Š” ์œ„์น˜ ํ‚ค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ํ•ด๋‹น ์œ„์น˜๋กœ ์ง€๋„๊ฐ€ ์ด๋™, ์คŒ์ธ(zoom-in) ๋ฉ๋‹ˆ๋‹ค.


โœจ ์‚ฌ์šฉ์ž ํ˜„์žฌ ์œ„์น˜ ๊ฒ€์ƒ‰


geolocation์„ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ์œ„์น˜๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


โœจ ๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ ์ง€์›

ํ—ค๋” ๋ถ€๋ถ„์˜ switch๋ฅผ ํ†ตํ•ด ๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


โœจ ๋ฐ˜์‘ํ˜• ์›น

๋ฐ์Šคํฌํƒ‘, ์•„์ดํŒจ๋“œ, ๋ชจ๋ฐ”์ผ ์ˆœ์œผ๋กœ ๋ฐ˜์‘ํ˜• ์›น์„ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.



๐ŸŒฟ ํ›„๊ธฐ

๊ณต๊ณต๊ธฐ๊ด€์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜คํ”ˆ ๋ฐ์ดํ„ฐ api๋ฅผ ์–ด๋–ป๊ฒŒ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ ์ง€๋„ ์œ„์— ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ๊ณ ๋ฏผํ•˜๋ฉฐ fetch ์ž‘๋™ ์›๋ฆฌ๋ฅผ ์ดํ•ด ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ ,
์นด์นด์˜ค map API์—์„œ ์ œ๊ณตํ•˜๋Š” ์ง€๋„ํ‘œ์‹œ, ํด๋Ÿฌ์Šคํ„ฐ, ๋งˆ์ปค๊ธฐ๋Šฅ, ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๋“ฑ์˜ ๋ฌธ์„œ๋ฅผ ์ฝ๊ณ , ๋ฆฌ์•กํŠธ์— ์ ์šฉ์‹œํ‚ค๋Š” ๊ณผ์ •์—์„œ ๋ฆฌ์•กํŠธ๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ์— ๋„์›€์ด ๋งŽ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Seoulite ์ž‘๋™ํ™”๋ฉด์„ ๋ณด๋ฉด, ํ™”๋ฉด์€ ํฌ๊ฒŒ Header๋ถ€๋ถ„๊ณผ ์ง€๋„ Map ๋ถ€๋ถ„ ๋‘ ๊ฐ€์ง€๋กœ ๋‚˜๋ˆ„์–ด ์ง‘๋‹ˆ๋‹ค.
Header๋ถ€๋ถ„๊ณผ Map์„ ๋ณด์—ฌ์ฃผ๋Š” MapContainer๋ถ€๋ถ„์„ ์ปดํฌ๋„ŒํŠธํ™” ์‹œ์ผœ ๋ถ„๋ฆฌ์‹œํ‚ค๋Š” ๊ณผ์ •์—์„œ
๋ฆฌ์•กํŠธ ์ƒ์—์„œ ๊นŒ๋‹ค๋กœ์šด ๋ถ€๋ชจ์™€ ์ž์‹, ํ˜•์ œ๊ด€๊ณ„ ์‚ฌ์ด์˜ ๋ฐ์ดํ„ฐ ๊ตํ™˜์„ ์ดํ•ดํ•˜๋Š”๋ฐ์— ๋งŽ์€ ์–ด๋ ค์›€์„ ๊ฒช์–ด ๊ณต๋ถ€๋ฅผ ํ•˜๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ,

๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์ด์˜ ๋ฐ์ดํ„ฐ ๊ตํ™˜ ์ฐธ๊ณ  ๋งํฌ

์œ„์˜ ๊ธ€์ด ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์ด์˜ ๋ฐ์ดํ„ฐ๊ตํ™˜์„ ์ดํ•ดํ•˜๋Š”๋ฐ์— ์ •๋ง ๋งŽ์€ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
๋ถ€๋ชจ์—์„œ ์ž์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ์€ props๋ฅผ ํ†ตํ•ด ์‰ฝ๊ฒŒ ์ „๋‹ฌ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋ฐ˜๋Œ€๋Š” ์กฐ๊ธˆ ๋” ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ฒจ์ฃผ๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด** callback function์„ ์ด์šฉ**ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class Parent extends React.Component {
state = { message: "" }
callbackFunction = (childData) => {
      this.setState({message: childData})
},
render() {
        return (
            <div>
                 <Child1 parentCallback = {this.callbackFunction}/>
                 <p> {this.state.message} </p>
            </div>
        );
}
}

ํ˜•์ œ์ž๋งค ์‚ฌ์ด์˜(Between Siblings) ๋ฐ์ดํ„ฐ ๊ตํ™˜์€ ๋”์šฑ ๋ณต์žกํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด๋Š”** Redux ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ** ๋˜๋Š” ๋ฆฌ์•กํŠธ์˜ context API๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•ด๊ฒฐ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ props์™€ callback function ๋‘ ๊ฐ€์ง€ ๋ฐ์ดํ„ฐ ๊ณต์œ  ๋ฐฉ๋ฒ•์„ ๊ฒฐํ•ฉํ•˜์—ฌ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, ์ปดํฌ๋„ŒํŠธ ์‚ฌ์ด์˜ ๊ด€๊ณ„๊ฐ€ ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค๋ฉด ์ด๋Š” ๊ตฌํ˜„ํ•˜๊ธฐ ๊ต‰์žฅํžˆ ๊นŒ๋‹ค๋กœ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Seoulite๋Š” ๊ฐ„๋‹จํ•œ ์ง€๋„ ์„œ๋น„์Šค์ด๊ธฐ ๋•Œ๋ฌธ์—, Redux๋ฅผ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์€ ๋„ˆ๋ฌด ๋ฌด๊ฑฐ์šธ ์ˆ˜ ์žˆ๋‹ค ํŒ๋‹จ์„ ํ•˜์—ฌ, ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ๊ตํ™˜์— ๋Œ€ํ•œ ๊ธฐ๋ณธ์ ์ธ ์ดํ•ด๋ฅผ ์œ„ํ•ด ์„ธ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์„ ํƒํ•˜์—ฌ ์ ์šฉ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

[Seoulite ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ ์ผ๋ถ€] ๋ฐ์ดํ„ฐ ๊ตํ™˜์„ ์œ„ํ•ด ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ(App.js)์—์„œ callback ํ•จ์ˆ˜ ์‚ฌ์šฉ


๊ฐ€์žฅ ์žฌ๋ฏธ์žˆ๊ณ  ํ•ต์‹ฌ์ด์˜€๋˜ ๋ถ€๋ถ„์€ Kakao map API๋ฅผ ์ด์šฉํ•˜์—ฌ ์ง€๋„๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ํŒจ์น˜ํ•˜์—ฌ ๋งˆ์ปค๋ฅผ ์ฐ์–ด์ฃผ๊ณ  ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ถ€๋ถ„์ด์˜€์Šต๋‹ˆ๋‹ค.
axios๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ํŒจ์น˜ํ•˜์˜€๋Š”๋ฐ,
๋ฌธ์ œ์ ์€ ์„œ์šธ์‹œ ์—ด๋ฆฐ๋ฐ์ดํ„ฐ ๊ด‘์žฅ์ด ํ•œ๋ฒˆ์˜ ์š”์ฒญ์ด ์ตœ๋Œ€1000๊ฐœ ๊นŒ์ง€๋งŒ ํ—ˆ์šฉ(์‚ฌ์šฉํ•˜๋ ค๋Š” data์˜ ๊ฐœ์ˆ˜๋Š” 1993๊ฐœ ์˜€์Œ)๋˜์–ด์„œ totalCount๊ฐ€ end ๊ฐ’์„ ๋„˜์œผ๋ฉด ๊ทธ ์ดํ›„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์•„๋ž˜์™€ ๊ฐ™์ด if๋ฌธ์„ ์ž‘์„ฑํ•˜์—ฌ recursive function ์žฌ๊ท€ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ „์ฒด locations ๊ฐ’์„ ๊ตฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์•„๋ž˜ ์ด๋ฏธ์ง€๋Š”
๋ฆฌ์•กํŠธ์˜ useRef๋ฅผ ์ด์šฉํ•ด Dom์š”์†Œ์— ์ ‘๊ทผํ•ด map์„ ์ƒ์„ฑํ•˜๋Š” ๋ถ€๋ถ„ ์ž…๋‹ˆ๋‹ค.
[mapContainer.js ์ฝ”๋“œ ์ผ๋ถ€]


๋˜ ์š”์ฆ˜ ๋งŽ์€ ์›น์„œ๋น„์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” dark/light mode ๋ถ€๋ถ„์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ๋„ ์–ด๋ ค์›€์„ ๊ฒช์—ˆ๋Š”๋ฐ, ๊ทธ๋™์•ˆ SCSS์˜ ๋ฐฉ์‹์— ์ต์ˆ™ํ•ด์ ธ ์žˆ๋‹ค๊ฐ€, css-in-js ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ๋ฐฉ์‹์˜ styled-components๋ฅผ ๋„์ž…ํ•˜๋ฉด์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€๋ฅผ ์‹œ๋„ํ•ด๋ณด์•„์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.(ThemeProvider, GlobalStyle, props ๋“ฑ์— ๋Œ€ํ•˜์—ฌ ๋ฐฐ์›€)

dark/light mode๋ฅผ ๋งŒ๋“ค๋ฉด์„œ ํฅ๋ฏธ๋กœ์› ๋˜ ์ ์€ App.js(๋ถ€๋ชจ) ๋‚ด์—์„œ ์“ฐ์—ฌ์ง„ ThemeProvider์˜ theme์— Header.js(์ž์‹)์—์„œ ์ ‘๊ธ‰ํ•˜์—ฌ ํ—ค๋” ์ƒ‰์„ ๋ฐ”๊พธ์–ด์ฃผ๊ณ  ์‹ถ์–ด ์ฐพ์•„๋ณด๋‹ˆ
withTheme(higher-order component) HOC์„ ์ด์šฉํ•œ ๋ฐฉ์‹๊ณผ useContext๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ์žˆ์–ด useContext๋ฅผ ์ ์šฉ์‹œ์ผœ ๋ณด์•˜๋Š”๋ฐ, styled-components ์—์„œ ์ œ๊ณตํ•˜๋Š” ThemeContext๋ฅผ ์ด์šฉํ•˜๋ฉด, createContext๋กœ ์ง์ ‘ context๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์•„๋„ useState๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด์˜€์Šต๋‹ˆ๋‹ค.
์•„๋ž˜ ์ด๋ฏธ์ง€๋Š” useContext๋ฅผ ์ด์šฉํ•ด ๋งŒ๋“  Dark/light mode์˜ Header.js ํŒŒ์ผ ์ผ๋ถ€ ์ž…๋‹ˆ๋‹ค.

styled-commponents withTheme HOC & useContext ์ฐธ๊ณ  ๋งํฌ


geolocation์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ์œ„์น˜๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋ถ€๋ถ„๋„ ํฅ๋ฏธ๋กœ์› ์Šต๋‹ˆ๋‹ค.

Seoulite ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด ๋ฆฌ์•กํŠธ๋ฅผ ์ด์šฉํ•œ ๊ฐ„๋‹จํ•œ ์ง€๋„ ์„œ๋น„์Šค์ด์ง€๋งŒ, ๋ฆฌ์•กํŠธ์˜ ์ •๋ง ๋งŽ์€ ์‚ฌ์šฉ๋ฒ•๊ณผ ์ž‘๋™ ์›๋ฆฌ๋ฅผ ์ดํ•ด ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ์–ด๋ ต๊ฒŒ ๋Š๊ปด์กŒ๋˜ ๋ฆฌ์•กํŠธ๊ฐ€ ์กฐ๊ธˆ์”ฉ ์ต์ˆ™ํ•ด์ง„๋‹ค ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

profile
๊ทธ๋ฆผ๋„ ๊ทธ๋ฆฌ๋Š” ๊ฐœ๋ฐœ์ž ๐ŸŽจ๐Ÿ‘ฉโ€๐Ÿ’ป

6๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2021๋…„ 2์›” 8์ผ

Netlify๋กœ ๋ฐฐํฌํ•˜์‹ค ๋•Œ ํ˜น์‹œ ๋„๋ฉ”์ธ ๋“ฑ๋ก ํ•˜์…จ๋Š”์ง€ ์—ฌ์ญค๋ด๋„ ๋ ๊นŒ์š”?

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2021๋…„ 2์›” 26์ผ

๋ฉ‹์ง„ ํ”„๋กœ์ ํŠธ๋„ค์š”! ์ฝ”๋“œ ์ค‘๊ฐ„์ค‘๊ฐ„ ์ƒ๊ฐ์˜ ํ๋ฆ„? ๊ฐ™์€๊ฑฐ ์ ์–ด์ฃผ์‹ ๊ฑฐ ์žฌ๋ฏธ์žˆ๋„ค์š” ใ…Žใ…Ž

1๊ฐœ์˜ ๋‹ต๊ธ€