๐Ÿท๏ธ [ ๋ชฉ์ฐจ ]

  • HTTP ํ†ต์‹ 
  • HTTP ์š”์ฒญ(Request)๊ณผ ์‘๋‹ต(Response)
    - ์š”์ฒญ(Request)
    - ์‘๋‹ต(Response)
  • API
  • API ์ข…๋ฅ˜ (rest-API vs graphql-API)
    - rest-API์™€ graphql์˜ ์ฐจ์ด์ 
  • API ์‘๋‹ต ๋ฐ์ดํ„ฐ JSON ( JavaScript Object Notation )
  • API์™€ CRUD
  • GRAPHQL _ playGround

๐Ÿ–‡๏ธ [์ถœ์ฒ˜ ๋ฐ ์ฐธ์กฐ]

์ฝ”๋“œ์บ ํ”„
https://developer.mozilla.org/ko/docs/Web/HTTP/Overview

๐Ÿ’ก HTTP ํ†ต์‹ 

HTTP๋ž€โ“ ๋‘ ์ปดํ“จํ„ฐ ๊ฐ„(ํ”„๋ก ํŠธ <-> ๋ฐฑ์—”๋“œ)์— ํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ๊ธธ์ด๋‹ค.
ํด๋ผ์ด์–ธํŠธ(ex: ๋ธŒ๋ผ์šฐ์ €)์™€ ์„œ๋ฒ„๊ฐ€ HTTP ๋ผ๋Š” ๊ธธ๋กœ ์š”์ฒญ(request)๊ณผ ์‘๋‹ต(response) 2๊ฐ€์ง€๋ฅผ ์„œ๋กœ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ’ก HTTP ์š”์ฒญ(Request)๊ณผ ์‘๋‹ต(Response)

โœ… HTTP ์š”์ฒญ(Request)

์›น๋ธŒ๋ผ์šฐ์ €์—์„œ ํ™ˆํŽ˜์ด์ง€(Front-end)๊ฐ€ ์‹คํ–‰์ค‘์ด๋ผ๋ฉด, ์ž‘์„ฑํ•œ ๊ฒŒ์‹œ๋ฌผ ํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ HTTP๋ฅผ ํ†ตํ•ด Back-end ์ปดํ“จํ„ฐ๋กœ ๋ณด๋‚ด๊ณ  Back-end ์ปดํ“จํ„ฐ์—๊ฒŒ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ ํ•ด๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ•œ๋‹ค.

โœ… HTTP ์‘๋‹ต(Response)

์š”์ฒญ์„ ๋ฐ›์€ Back-end ์ปดํ“จํ„ฐ๋Š” ์„ฑ๊ณต, ์‹คํŒจ ๋“ฑ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์‘๋‹ตํ•œ๋‹ค.
Back-end ์ปดํ“จํ„ฐ๋Š” ์‘๋‹ตํ•  ๋•Œ ์‘๋‹ต ์ƒํƒœ์ฝ”๋“œ ๋ผ๋Š” ๊ฒƒ๋„ ํ•จ๊ป˜ ๋ณด๋‚ด์ฃผ๋Š”๋ฐ,
์‘๋‹ต ์ƒํƒœ์ฝ”๋“œ๋Š” 100~ 599๊นŒ์ง€์˜ ์ˆซ์ž๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.
์ž์ฃผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์‘๋‹ต ์ƒํƒœ์ฝ”๋“œ๋Š” ์„ฑ๊ณต(200), Front-end ์—๋Ÿฌ(400), Back-end ์—๋Ÿฌ(500) ๋“ฑ์ด ์žˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, ์š”์ฒญ์— ์„ฑ๊ณตํ•˜์˜€์œผ๋ฉด ์„ฑ๊ณต ๋ฉ”์‹œ์ง€์™€ ์‘๋‹ต ์ƒํƒœ์ฝ”๋“œ 200์„ ํ•จ๊ป˜ ๋ณด๋‚ด์ฃผ๊ฒŒ ๋˜๋Š” ๊ฒƒ!

โœ… ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ”๋กœ ์š”์ฒญํ•˜๋ฉด ์•ˆ๋˜๋Š” ๊ฑธ๊นŒโ“

๋ณด์•ˆ ๋ฐ ๋ฐ์ดํ„ฐ ์ •์ œ ๋“ฑ์˜ ์ด์œ ๋กœ Back-end์—์„œ ๊ฒ€์ฆ ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์•„๋ฌด๋‚˜ ํ•จ๋ถ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์š”์ฒญํ•  ์ˆ˜ ์—†๋‹ค!


๐Ÿ’ก API

โœ… API๋ž€โ“

HTTP ์š”์ฒญ์„ Back-end ์ปดํ“จํ„ฐ์— ๋ณด๋ƒˆ์„๋•Œ ์‹คํ–‰๋˜๋Š” Back-end ๊ธฐ๋Šฅ์ด๋‹ค.
Front-end์—์„œ HTTP๋ผ๋Š” ๊ธธ์„ ํ†ตํ•ด ๊ฒŒ์‹œ๋ฌผ ๋ฐ์ดํ„ฐ๋ฅผ Back-end์— ๋ณด๋‚ด ์ €์žฅ์‹œํ‚ค๋Š”๋ฐ,
๋งŒ์•ฝ ๊ฒŒ์‹œ๋ฌผ์ด ์•„๋‹Œ ํ”„๋กœํ•„ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒโ“
๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„ , ์—ฌ๋Ÿฌ๊ฐœ์˜ HTTP ๋ผ๋Š” ๊ธธ์ด ์กด์žฌํ•ด์•ผ ํ•˜๊ณ , ๊ฐ๊ฐ์˜ ์š”์ฒญ๋งˆ๋‹ค ๋‹ด๋‹น์ž๊ฐ€ ํ•„์š”ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ด ๋‹ด๋‹น์ž๋ฅผ API ๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๊ฒƒ์ด๋‹ค.

API๊ฐ€ ๊ธฐ๋Šฅ์ด๋ผ๋ฉด ๊ธฐ๋Šฅ์€ ๊ณง ํ•จ์ˆ˜๊ฐ€ ์•„๋‹๊นŒ โ“

API๋Š” Back-end ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งŒ๋“  ํ•จ์ˆ˜์ด๋‹ค! ํ•จ์ˆ˜๋Š” ์ธ์ž์™€ return ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๊ณ , API์— ์š”์ฒญํ•  ๋•Œ ๋ณด๋‚ด๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๊ณง API ํ•จ์ˆ˜๋กœ ๋“ค์–ด๊ฐˆ ์ธ์ž์ด๋ฉฐ
์‘๋‹ต์œผ๋กœ ๋ฐ›๊ฒŒ๋˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ API ํ•จ์ˆ˜์˜ return ๋ฐ์ดํ„ฐ ์ธ ๊ฒƒ์ด๋‹ค.


๐Ÿ’ก API ์ข…๋ฅ˜(rest-API vs graphql-API)

HTTP ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•ด ์ธํ„ฐ๋„ท์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๊ณ , ์ฃผ์†Œ(URL, URI)๋ฅผ ํ†ตํ•ด API ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
API์˜ ์ข…๋ฅ˜๋Š” ํฌ๊ฒŒ rest-API, graphql-API 2๊ฐ€์ง€๋กœ,
๋ช‡๊ฐ€์ง€ ์ฐจ์ด์ ์ด ์žˆ๋‹ค.

โœ… rest-API์™€ graphql์˜ ์ฐจ์ด์ 

1. ํ•จ์ˆ˜ ์ด๋ฆ„์˜ ์ฐจ์ด

2. ์‘๋‹ต ๊ฒฐ๊ณผ๋ฌผ์˜ ์ฐจ์ด

rest-API๋Š” ์‘๋‹ต ๊ฒฐ๊ณผ๋กœ back-end ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งŒ๋“  ํ•จ์ˆ˜์—์„œ ๋ณด๋‚ด์ฃผ๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์•ผ๋งŒ ํ•œ๋‹ค.
๋ฐ˜๋ฉด์—, graphql-API๋Š” back-end ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งŒ๋“  ํ•จ์ˆ˜์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๊ณจ๋ผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฌํ•œ ์ด์œ ๋กœ, ๊ฐ API์— ์ „์†ก์„ ์š”์ฒญํ•˜๋Š” ๋‹ด๋‹น์ž๋„ ๋‹ค๋ฅด๋‹ค.
rest-API์— ์š”์ฒญํ•˜๋Š” ์š”์ฒญ๋‹ด๋‹น์ž๋Š” axios์ด๊ณ 
graphql-API์— ์š”์ฒญํ•˜๋Š” ์š”์ฒญ๋‹ด๋‹น์ž๋Š” apollo-client์ด๋‹ค.
(์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” ์š”์ฒญ๋‹ด๋‹น์ž๋Š” Front-end ์—์„œ ์„ค์น˜ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.)

์ด๋ ‡๋“ฏ ๋‹ด๋‹น์ž๊ฐ€ graphql์ธ์ง€ rest์ธ์ง€์— ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ๋ฐ,
rest์˜ ๊ฒฝ์šฐ ์š”์ฒญ ์‹œ์— ๋ฐฑ์—”๋“œ๊ฐ€ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋†“๊ณ  ์„ธํŒ…ํ•ด๋†“์œผ๋ฉด
๋””๋น„์—์„œ ์ฐพ์•„์„œ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด ์‘๋‹ตํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—,
ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์›ํ• ๋•Œ๋„ ๋‹ค๋ฅธ ์ •๋ณด๋“ค์ด ํ•จ๊ป˜ ๋‹ฌ๋ ค์˜จ๋‹ค.
๋”ฐ๋ผ์„œ ์šฉ๋Ÿ‰์ด ๋งŽ์•„ ๋Š๋ ค์ง„๋‹ค.

๋ฐ˜๋ฉด์— graphql์€ ์š”์ฒญ์‹œ ์ •ํ™•ํžˆ ์–ด๋–ค ๊ฒƒ์ด๋ผ๊ณ  ํ•œ๋‹ค๋ฉด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๊ณจ๋ผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—,
๋ถˆํ•„์š”ํ•œ ๊ฒƒ๋“ค์„ ์‘๋‹ต๋ฐ›์ง€ ์•Š์„ ์ˆ˜ ์žˆ์–ด ํšจ์œจ์ ์ธ ํ†ต์‹ ์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

ํ•ต์‹ฌ์€, ์ฃผ๋Š”๋Œ€๋กœ ๋ฐ›๋Š”๋ƒ ๊ณจ๋ผ์„œ ๋ฐ›๋Š๋ƒ ์ด๋‹ค.
graphql์€ ํŽ˜์ด์Šค๋ถ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ˆ˜๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํŽ˜์ด์Šค๋ถ ๊ฐœ๋ฐœํŒ€์—์„œ ๋งŒ๋“ค์—ˆ์œผ๋ฉฐ,
facebook, airbnb, github ๋“ฑ ์œ ๋ช…ํ•œ ์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉ์ค‘์ธ ํ†ต์‹  ๋ฐฉ๋ฒ•์ด๋‹ค.

3. ์„ค์น˜ํ•ด์•ผ ํ•  ํ”„๋กœ๊ทธ๋žจ์˜ ์ฐจ์ด

rest-API ์™€ graphql API๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ๊ฐ ํ”„๋กœ๊ทธ๋žจ์„ ์„ค์น˜ํ•ด์•ผํ•œ๋‹ค.
์‚ฌ์ดํŠธ
ํฌ์ŠคํŠธ๋งจ
์Šค์›จ๊ฑฐ

โ—๏ธ์ •๋ฆฌ

[GRAPHQL-API]์˜ ์žฅ์ 

  1. ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๊ณจ๋ผ์„œ ๋ฐ›๊ธฐ ๊ฐ€๋Šฅ
  2. ๋ชจ๋‘ ๋ฐ›๋Š” ๊ฒƒ๋ณด๋‹ค ์šฉ๋Ÿ‰์ด ์ž‘์•„์„œ ๋น ๋ฆ„
  3. ํŽ˜์ด์Šค๋ถ, ๊นƒํ—ˆ๋ธŒ, ์—์–ด๋น„์•ค๋น„ ๋“ฑ ๊ธ€๋กœ๋ฒŒ ์„œ๋น„์Šค ์‚ฌ์šฉ

[REST-API]๋ฅผ ์•Œ์•„์•ผ๋งŒ ํ•˜๋Š” ์ด์œ 

  1. ๋งŽ์€ ํšŒ์‚ฌ๊ฐ€ REST API๋ฅผ ์‚ฌ์šฉ
  2. ์ทจ์—…ํ•œ ํšŒ์‚ฌ์—์„œ REST๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.
  3. OPEN-API(= PUBLIC API)์—์„œ ์ผ๋ฐ˜์ ์œผ๋กœ REST๋ฅผ ์ œ๊ณต
    ex) ์นด์นด์˜ค๋กœ ๋กœ๊ทธ์ธํ•˜๊ธฐ ๊ธฐ๋Šฅ์€ ์นด์นด์˜ค์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” API๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”๋ฐ, ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ๋“ค์€ ๋Œ€๋ถ€๋ถ„ rest-API๋กœ ์ œ๊ณต๋˜๊ณ  ์žˆ๋‹ค.

๐Ÿ’ก API ์‘๋‹ต ๋ฐ์ดํ„ฐ JSON ( JavaScript Object Notation )

โœ… JSON์ด๋ž€โ“

๋ฐฑ์—”๋“œ์—์„œ ์‘๋‹ต์„ ๋ฐ›์„ ๋•Œ, ์ฒ˜์Œ์—” ๋ฌธ์ž์—ดํ˜•ํƒœ์˜ ๊ฐ์ฒดํ˜•ํƒœ์ด๋‹ค -> {""}
ํ•˜์ง€๋งŒ ๊ทธ ๊ฐ’์„ "ํ…์ŠคํŠธ"๋กœ ๋ฐ–์— ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ฒด ์ž์ฒด๋กœ๋Š” ๋ฐ›์„ ์ˆ˜๋Š” ์—†๋‹ค.

๋”ฐ๋ผ์„œ ๊ฐ์ฒด๋ฅผ "๋ฌธ์ž์—ด"๋กœ ๋ฌถ์–ด ์ž‘์—…์„ ํ•œ ๋’ค ๊ฐ์ฒด๋ฅผ ๋‹ด์€ ๋ฌธ์ž์—ด "{""}" ์„ ๋ณด๋‚ด๊ฒŒ ๋˜๋Š”๋ฐ
์ด ๋•Œ ๊ฐ์ฒด๋ฅผ ๋‹ด์€ ๋ฌธ์ž์—ด์„ ๊ฐ์ฒด ํ‘œ๊ธฐ๋ฒ• ์ด๋ผ๊ณ  ํ•˜๋ฉฐ JSON ์ด๋ผ๊ณ  ํ•œ๋‹ค!
๊ทธ ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํ„ฐ์— ์ €์žฅ์„ ํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ถœ๋ ฅํ•˜๋ฉด ๊ฐ์ฒด๋‚ด์šฉ์ด ๋‚˜์˜จ๋‹ค.

์ฆ‰, ๋ฐฑ์—”๋“œ์—์„œ ์‘๋‹ต์˜ ๊ฒฐ๊ณผ๋ฌผ๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ์ด JSON์ด๊ณ ,
๋ฐ›์•„์˜จ JSON ๋ฐ์ดํ„ฐ๋Š” ํ”„๋ก ํŠธ์—์„œ ๋ฌธ์ž์—ด์„ ๋ฒ—๊ฒจ ๊ฐ์ฒด๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

โœ… JSON์˜ ํŠน์ง•๊ณผ ์‘๋‹ตํ—ค๋”

์‘๋‹ต์€ header์™€ body๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋‰˜๊ฒŒ ๋œ๋‹ค.
์‘๋‹ต์œผ๋กœ ์ฃผ๊ณ ๋ฐ›์€ JSON์€ ์‘๋‹ต์˜ body ๋ถ€๋ถ„์ด๊ณ ,
header์—๋Š” body์™€ ๊ด€๋ จ๋œ ์š”์•ฝ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค.
์—ฌ๊ธฐ์„œ ์š”์•ฝ์ •๋ณด๋ผ ํ•จ์€ ์‘๋‹ต์„ ๋ณด๋‚ด๋Š” ์‚ฌ์ดํŠธ๊ฐ€ ์–ด๋””์ธ์ง€,
๋ฐ”๋””์˜ ํ˜•ํƒœ๋Š” JSON์ธ์ง€ ๋“ฑ๋“ฑ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์‘๋‹ต์„ ํŽธ์ง€๋ด‰ํˆฌ๋ผ๊ณ  ์น˜๋ฉด
ํŽธ์ง€๋ด‰ํˆฌ๋Š” (์š”์ฒญ)ํ—ค๋”์™€ ๋ฐ”๋””๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๊ณ 
์š”์ฒญํ—ค๋”์˜ ๋ณด๋‚ด๋Š” ์ด๋Š” ๋ธŒ๋ผ์šฐ์ €, ๋ณด๋‚ด๋Š” ํ˜•ํƒœ๋Š” ์š”์•ฝํ•œ ๊ฐ์ฒด์ž„.
๋ฐฑ์—”๋“œ๋Š” ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ํ•œ๋‹ค.
๋ธŒ๋ผ์šฐ์ €๋Š” ์„ฑ๊ณตํ–ˆ๋Š”์ง€ ์‹คํŒจํ–ˆ๋Š”์ง€ ์•Œ์•„์•ผ ํ•œ๋‹ค.
์‚ฌ์ดํŠธ

-> ํ•˜์ง€๋งŒ ๋งค๋ฒˆ ๋ฐ›์„๋•Œ๋งˆ๋‹ค ๋ฌธ์ž์—ด์„ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•˜๋Š”๋ฐ
๊ทธ๊ฑด ๊ท€์ฐฎ์œผ๋‹ˆ๊นŒ ์—”์‹œ์˜ค์Šค ์ž์ฒด์—์„œ ์ฒ˜๋ฆฌํ•ด์คŒ.
์•ˆํ’€๋ ค์„œ ๋‚˜์˜ค๋Š” ๋ฌธ์ž๋“ค์€ ์ง์ ‘ ๋ฐ”๊ฟ”์„œ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค!
๋ฌธ์ž์—ด ์ง€์šฐ๋Š” ๋ช…๋ น์–ด Json.parse


๐Ÿ’ก API์™€ CRUD

โœ… API์˜ 4๊ฐ€์ง€ ๋ฐฉ์‹

  1. ์ƒˆ๋กœ์šด ๊ฒƒ์„ ์ƒ์„ฑํ•˜๋Š” API โ‡’ CREATE
  2. ๊ธฐ์กด์˜ ๊ฒƒ์„ ์กฐํšŒํ•˜๋Š” API โ‡’ READ
  3. ๊ธฐ์กด์˜ ๊ฒƒ์„ ์ˆ˜์ •ํ•˜๋Š” API โ‡’ UPDATE
  4. ๊ธฐ์กด์˜ ๊ฒƒ์„ ์‚ญ์ œํ•˜๋Š” API โ‡’ DELETE
    -> DB๋ฅผ ๋ฐ”๊ฟ€๊ฒƒ์ธ์ง€ ๊ทธ๋Œ€๋กœ ๋†”๋‘˜๊ฒƒ์ธ์ง€ ํ•˜๊ธฐ ์œ„ํ•ด mutation๊ณผ query๋กœ ๋‚˜๋‰˜๊ฒŒ ๋˜์—ˆ์Œ.

โœ… ์‚ฌ์šฉ๋ฐฉ๋ฒ•

โœ… ํ•œ ๋ˆˆ ์ •๋ฆฌ


๐Ÿ’ก API ๋ช…์„ธ์„œ

API ๋ช…์„ธ์„œ๋Š” API ์‚ฌ์šฉ ์„ค๋ช…์„œ๋กœ,
ํ™ˆํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๊ธฐ ์ „, Back-end ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งŒ๋“ค์–ด ๋†“์€ API ๊ฐ€ ๋ช‡ ๊ฐœ ์žˆ๊ณ ,
์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”๋กœ ํ•œ๋‹ค.
`API๋ช…์„ธ์„œ๋Š” Back-end ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋ฐ›์•„์•ผ ํ•˜๋Š”๋ฐ,
Back-end ๊ฐœ๋ฐœ์ž๋Š” ์ž์‹ ์ด ๋งŒ๋“  API๋ฅผ ์ง์ ‘ ๋ฌธ์„œ ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜,
swagger ๋ผ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ์„ค์น˜ํ•ด์„œ ๋งŒ๋“ ๋‹ค.


๐Ÿ’กGRAPHQL _ playGround

โœ… graphQl docs์˜ ํ•„์ˆ˜๊ฐ’

! ๋Š” ๋ฌด์กฐ๊ฑด ๋ณด๋‚ด์ค˜์•ผํ•˜๋Š” ํ•„์ˆ˜๊ฐ’
[!]๋Š” ์žˆ๋‹ค๋ฉด ๋ฌด์กฐ๊ฑด ๋ณด๋‚ด์ค˜์•ผํ•˜๋Š” ์กฐ๊ฑดํ•„์ˆ˜๊ฐ’

โœ… Mutation

: ์ƒˆ๋กœ์šด data๋ฅผ ๋งŒ๋“ค๋•Œ mutation์„ ์‚ฌ์šฉํ•œ๋‹ค.
->์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œ, ์กฐํšŒ์— ๋”ฐ๋ฅธ ์š”์ฒญ ๋ช…๋ น์–ด๊ฐ€ ์žˆ๋Š”๋ฐ
GRAPHQL์€ ์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œ๋ฅผ ํ•˜๋‚˜๋กœ ํ†ตํ•ฉํ•œ๋‹ค.
๊ทธ๋Ÿฌ๊ธฐ์— MUTATION์€ ์‹ ์ค‘ํ•ด์•ผ ๋œ๋‹ค.

โœ… Query

: ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒ ์‹œ query๋ฌธ์„ ์‚ฌ์šฉ


โ—๏ธ์ถ”๊ฐ€ํ•™์Šต์ฃผ์ œ

1. HTTP ์‘๋‹ต ์ƒํƒœ์ฝ”๋“œ 404๋Š” ๋ฌด์—‡์„ ์˜๋ฏธํ•˜๋Š”๊ฐ€?
http://yoonbumtae.com/?p=4932
2. HTTP๋ฉ”์„œ๋“œ PUT๊ณผ PATCH๋Š”์˜ ์ฐจ์ด๋Š”?
https://programmer93.tistory.com/39
3. rest-API์˜ ์–ธ๋”ํŽ˜์นญ, ์˜ค๋ฒ„ํŽ˜์นญ์ด๋ž€?https://ivvve.github.io/2019/07/24/server/graphql/over-under-fetching/
https://stackoverflow.com/questions/44564905/what-is-over-fetching-or-under-fetching


๐Ÿ“Œ ์‹ค์Šต

JSX

 // import { Header } from "antd/es/layout/layout";
import {
  Wrapper,
  Title,
  Wrapper_input,
  Wrapper_info,
  Name,
  Input,
  Password,
  Wrapper_title,
  Subtitle,
  Sub_Input,
  Wrapper_content,
  Content,
  Content_input,
  Wrapper_address,
  Address_title,
  AddressZip,
  Address_Input,
  Search_btn,
  Address,
  Wrapper_youtube,
  Youtube_title,
  Youtube_Input,
  Wrapper_image,
  Img_title,
  UploadBox,
  UploadBtn,
  Box,
  Plus,
  Text,
  Wrapper_mainSet,
  Main_title,
  Radio,
  RadioButton,
  RadioLabel,
  Wrapper_register,
  RegisterBtn,
  Color,
} from "./css";
import { useState } from "react";
export default function freeBoardFrom() {
  const [TheName, setName] = useState("");
  const [ThePassword, setPassword] = useState("");
  const [TheSubtitle, setSubtitle] = useState("");
  const [TheContent, setContent] = useState("");
  const [NameError, setNameError] = useState("");
  const [PasswordError, setPasswordError] = useState("");
  const [SubtitleError, setSubtitleError] = useState("");
  const [ContentError, setContentError] = useState("");
  function onChangeName(event) {
    setName(event.target.value);
  }
  function onChangePassword(event) {
    setPassword(event.target.value);
  }
  function onChangeSubtitle(event) {
    setSubtitle(event.target.value);
  }
  function onChangeContent(event) {
    setContent(event.target.value);
  }
  function Signup() {
    //๊ฐ€์ž…ํ•˜๊ธฐ
    if (TheName === "") {
      //์ด๋ฆ„์ด ๋นˆ๊ฐ’์ด๋ผ๋ฉด
      setNameError("์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”");
    } else {
      setNameError("");
    }
    if (ThePassword === "") {
      setPasswordError("๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”");
    } else {
      setPasswordError("");
    }
    if (TheSubtitle === "") {
      setSubtitleError("์ œ๋ชฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”");
    } else {
      setSubtitleError("");
    }
    if (TheContent === "") {
      setContentError("๋‚ด์šฉ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”");
    } else {
      setContentError("");
    }
    if (
      TheName !== "" &&
      ThePassword !== "" &&
      TheSubtitle !== "" &&
      TheContent !== ""
    ) {
      alert("๊ฒŒ์‹œ๊ธ€์ด ๋“ฑ๋ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
    }
  }
  return (
    <Wrapper>
      <Title>๊ฒŒ์‹œ๋ฌผ๋“ฑ๋ก</Title>
      <Wrapper_input>
        <Wrapper_info>
          <Name>์ž‘์„ฑ์ž</Name>
          <Input
            type="text"
            placeholder="์ด๋ฆ„์„ ์ ์–ด์ฃผ์„ธ์š”"
            onChange={onChangeName}
          />
          <Color>{NameError}</Color>
        </Wrapper_info>
        <Wrapper_info>
          <Password>๋น„๋ฐ€๋ฒˆํ˜ธ</Password>
          <Input
            type="password"
            placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”"
            onChange={onChangePassword}
          />
          <Color>{PasswordError}</Color>
        </Wrapper_info>
      </Wrapper_input>
      <Wrapper_title>
        <Subtitle>์ œ๋ชฉ</Subtitle>
        <Sub_Input
          type="text"
          placeholder="์ œ๋ชฉ์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”"
          onChange={onChangeSubtitle}
        />
        <Color>{SubtitleError}</Color>
      </Wrapper_title>
      <Wrapper_content>
        <Content>๋‚ด์šฉ</Content>
        <Content_input
          type="text"
          placeholder="์ œ๋ชฉ์„ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”"
          onChange={onChangeContent}
        />
        <Color>{ContentError}</Color>
      </Wrapper_content>
      <Wrapper_address>
        <Address_title>์ฃผ์†Œ</Address_title>
        <AddressZip>
          <Address_Input type="text" placeholder="07250"></Address_Input>
          <Search_btn>์šฐํŽธ๋ฒˆํ˜ธ ๊ฒ€์ƒ‰</Search_btn>
        </AddressZip>
        <Address />
        <Address />
      </Wrapper_address>
      <Wrapper_youtube>
        <Youtube_title>์œ ํŠœ๋ธŒ</Youtube_title>
        <Youtube_Input
          type="text"
          placeholder="๋งํฌ๋ฅผ ๋ณต์‚ฌํ•ด์ฃผ์„ธ์š”."
</Youtube_Input>
      </Wrapper_youtube>
      <Wrapper_image>
        <Img_title>์‚ฌ์ง„์ฒจ๋ถ€</Img_title>
        <UploadBox>
          <UploadBtn>
            <Box>
              <Plus>+</Plus>
              <Text>Upload</Text>
            </Box>
          </UploadBtn>
          <UploadBtn>
            <Box>
              <Plus>+</Plus>
              <Text>Upload</Text>
            </Box>
          </UploadBtn>
          <UploadBtn>
            <Box>
              <Plus>+</Plus>
              <Text>Upload</Text>
            </Box>
          </UploadBtn>
        </UploadBox>
      </Wrapper_image>
      <Wrapper_mainSet>
        <Main_title>๋ฉ”์ธ์„ค์ •</Main_title>
        <Radio>
          <RadioButton type="radio" id="youtube" name="radio-button" />
          <RadioLabel>์œ ํŠœ๋ธŒ</RadioLabel>
          <RadioButton type="radio" id="image" name="radio-button" />
          <RadioLabel>์‚ฌ์ง„</RadioLabel>
        </Radio>
      </Wrapper_mainSet>
      <Wrapper_register>
        <RegisterBtn onClick={Signup}>๋“ฑ๋กํ•˜๊ธฐ</RegisterBtn>
      </Wrapper_register>
    </Wrapper>
  );
} 

CSS

import styled from "@emotion/styled";
export const Wrapper = styled.div`
  width: 1200px;
  /* height: 1847px; */
  background: #ffffff;
  box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);
  border: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-left: 102px;
  padding-right: 102px;
`;
export const Title = styled.div`
  width: 100%;
  font-style: normal;
  font-weight: 700;
  font-size: 36px;
  text-align: center;
  color: #000000;
  padding-top: 60px;
  padding-bottom: 80px;
`;
export const Wrapper_input = styled.div`
  width: 100%;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
`;
export const Wrapper_info = styled.div`
  width: calc(50% - 24px);
  position: relative;
`;
export const Name = styled.div`
  font-family: "Noto Sans CJK KR";
  font-style: normal;
  font-weight: 500;
  font-size: 16px;
  line-height: 24px;
  color: #000000;
  padding-bottom: 16px;
`;
export const Input = styled.input`
  width: 100%;
  height: 52px;
  background: #ffffff;
  border: 1px solid #bdbdbd;
  padding-left: 10px;
  &::placeholder {
    color: #c4c4c4;
    font-size: 16px;
    line-height: 24px;
  }
`;
export const Password = styled.div`
  font-family: "Noto Sans CJK KR";
  font-style: normal;
  font-weight: 500;
  font-size: 16px;
  line-height: 24px;
  color: #000000;
  padding-bottom: 16px;
`;
export const Wrapper_title = styled.div`
  width: 100%;
  margin-top: 40px;
  /* padding-top: 40px; */
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: relative; //error
`;
export const Subtitle = styled.div`
  font-family: "Noto Sans CJK KR";
  font-style: normal;
  font-weight: 500;
  font-size: 16px;
  line-height: 24px;
  color: #000000;
  padding-bottom: 16px;
`;
export const Sub_Input = styled.input`
  width: 100%;
  height: 52px;
  background: #ffffff;
  border: 1px solid #bdbdbd;
  padding-left: 10px;
  &::placeholder {
    color: #c4c4c4;
    font-size: 16px;
    line-height: 24px;
  }
`;
export const Wrapper_content = styled.div`
  width: 100%;
  /* margin-top:40px; */
  padding-top: 40px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  position: relative; //error
`;
export const Content = styled.div`
  font-family: "Noto Sans CJK KR";
  font-style: normal;
  font-weight: 500;
  font-size: 16px;
  color: #000000;
  padding-bottom: 16px;
`;
export const Content_input = styled.textarea`
  width: 100%;
  height: 480px;
  background: #ffffff;
  border: 1px solid #bdbdbd;
  padding-left: 10px;
  &::placeholder {
    color: #c4c4c4;
    font-size: 16px;
    line-height: 24px;
    padding-top: 10px;
  }
`;
export const Wrapper_address = styled.div`
  padding-top: 40px;
  display: flex;
  flex-direction: column;
  width: 100%;
  display: flex;
`;
export const Address_title = styled.div`
  font-family: "Noto Sans CJK KR";
  font-style: normal;
  font-weight: 500;
  font-size: 16px;
  color: #000000;
  padding-bottom: 16px;
`;
export const AddressZip = styled.div`
  display: flex;
  flex-direction: row;
`;
export const Address_Input = styled.input`
  width: 77px;
  height: 52px;
  background: #ffffff;
  border: 1px solid #bdbdbd;
  text-align: center;
  margin-right: 10px;
  &::placeholder {
    color: #c4c4c4;
    font-size: 16px;
    line-height: 24px;
    padding-top: 10px;
  }
`;
export const Search_btn = styled.button`
  width: 124px;
  background: #000000;
  border: 1px solid #bdbdbd;
  text-align: center;
  color: white;
`;
export const Address = styled.div`
  width: 100%;
  height: 52px;
  background: #ffffff;
  border: 1px solid #bdbdbd;
  padding-left: 10px;
  margin-top: 15px;
`;
export const Wrapper_youtube = styled.div`
  width: 100%;
  padding-top: 40px;
  display: flex;
  flex-direction: column;
  justify-content: center;
`;
export const Youtube_title = styled.div`
  font-family: "Noto Sans CJK KR";
  font-style: normal;
  font-weight: 500;
  font-size: 16px;
  color: #000000;
  padding-bottom: 16px;
`;
export const Youtube_Input = styled.input`
  width: 100%;
  height: 52px;
  background: #ffffff;
  border: 1px solid #bdbdbd;
  padding-left: 10px;
  &::placeholder {
    color: #c4c4c4;
    font-size: 16px;
    line-height: 24px;
    padding-top: 10px;
  }
`;
export const Wrapper_image = styled.div`
  width: 100%;
  padding-top: 40px;
  display: flex;
  flex-direction: column;
  justify-content: center;
`;
export const Img_title = styled.div`
  font-family: "Noto Sans CJK KR";
  font-style: normal;
  font-weight: 500;
  font-size: 16px;
  color: #000000;
  padding-bottom: 16px;
`;
export const UploadBox = styled.div`
  display: flex;
  flex-direction: row;
`;
export const UploadBtn = styled.div`
  display: flex;
  flex-direction: column;
`;
export const Box = styled.button`
  display: flex;
  flex-direction: column;
  background-color: black;
  width: 78px;
  height: 78px;
  background-color: #bdbdbd;
  margin-right: 24px;
  outline: none;
  border: none;
  cursor: pointer;
  align-items: center;
  justify-content: center;
`;
export const Plus = styled.div`
  font-size: 25px;
  color: #4f4f4f;
  font-weight: 500;
`;
export const Text = styled.div`
  color: #4f4f4f;
`;
export const Wrapper_mainSet = styled.div`
  width: 100%;
  padding-top: 40px;
  display: flex;
  flex-direction: column;
  justify-content: center;
`;
export const Main_title = styled.div`
  font-family: "Noto Sans CJK KR";
  font-style: normal;
  font-weight: 500;
  font-size: 16px;
  color: #000000;
  padding-bottom: 16px;
`;
export const Radio = styled.div`
  display: flex;
  flex-direction: row;
  border: none;
  background-color: white;
`;
export const RadioButton = styled.input`
  cursor: pointer;
`;
export const RadioLabel = styled.label`
  margin-left: 8px;
  margin-right: 20px;
  font-weight: 500;
  cursor: pointer;
`;
export const Wrapper_register = styled.div`
  width: 100%;
  padding-top: 40px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
`;
export const RegisterBtn = styled.button`
  width: 179px;
  height: 52px;
  border: none;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  background-color: yellow;
  margin-bottom: 80px;
`;
export const Color = styled.div`
  color: red;
  display: block;
  position: absolute;
  top: calc(100% + 6px);
`;

Image




๐ŸฅšMEMO

  1. ๋“ฑ๋ก,์ˆ˜์ •,์กฐํšŒ,์‚ญ์ œ๋ฅผ ์œ„ํ•œ ๊ฐ API๊ฐ€ ํ•„์š”ํ•˜๋‹ค
    API๋Š” ํ•˜๋‚˜ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ๋งˆ๋‹ค ๋”ฐ๋ผ๋‹ค๋‹ˆ๋Š” ํ•„์š”ํ•œ ๋ช…์„ธ์„œ์ด๋‹ค.
  2. ํŽ˜์ด์ง€๋„ ํ•˜๋‚˜์˜ ํฐ ์ปดํฌ๋„ŒํŠธ๋ผ๋Š” ๊ฒƒ ๊ธฐ์–ตํ•˜์ž!

๐ŸฅšQ&A

์–ด์ œ ๊ณผ์ œ ์ค‘ ๋ฏธ์™„์„ฑ ๋œ ๋ถ€๋ถ„์„ ์ฝ”๋“œ๋ฆฌ๋ทฐ ์‹œ๊ฐ„์„ ํ†ตํ•ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

Q. ์ž‘์„ฑ์ž์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ ์˜์—ญ ์‚ฌ์ด์˜ ๊ฐญ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•
A. ๋ถ€๋ชจ์š”์†Œ์˜ ๋„ˆ๋น„๋ฅผ 100%๋กœ ์ฃผ๊ณ  ์ž์‹์š”์†Œ๋“ค์„ ๊ฐ์‹ผ ์˜์—ญ์—
calcํ•จ์ˆ˜๋ฅผ ์จ์„œ 50% - ์ฃผ๊ณ  ์‹ถ์€ ๊ฐญ ์‚ฌ์ด์ฆˆ๋ฅผ ์ค˜์„œ ํ•ด๊ฒฐ

Q. input์˜์—ญ์˜ placeholder์— ํŒจ๋”ฉ์„ ์ฃผ์—ˆ๋”๋‹ˆ ์˜์—ญ ๋ฐ–์œผ๋กœ
์‚์ ธ๋‚˜๊ฐ€๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•จ.
A. &::placeholder ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์„œ input ์ž์ฒด๊ฐ€ ์•„๋‹Œ,
placeholder์—๋งŒ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๋„๋ก ํ•จ.

Q. contents์˜์—ญ์˜ placeholder๊ฐ€ ์ค‘๊ฐ„์— ๋จธ๋ฌผ๋Ÿฌ ์žˆ๋Š” ํ˜•์ƒ
A. textareaํƒœ๊ทธ๋กœ ๋ฐ”๊ฟ”์„œ ํ•ด๊ฒฐ.

Q. ๊ฐ ์ธํ’‹์— error๋ฉ”์‹œ์ง€๊ฐ€ ๋œจ๋ฉด ์•„๋ž˜๋กœ ๋ฐ€๋ฆฌ๋Š” ํ˜„์ƒ
A. error๋ฉ”์‹œ์ง€ ์š”์†Œ์—๋Š” display:block; position: absolute;์„ ์ฃผ๊ณ ,
์ƒ์œ„์š”์†Œ์— position: relative๋ฅผ ์ค˜์„œ ํ•ด๊ฒฐ
๋˜ํ•œ, calc(100% + 6px)ํ•จ์ˆ˜๋กœ ์ƒ๋‹จ์—์„œ 6px๋งŒํผ ๋–จ์–ด์ง€๋„๋ก ์ถ”๊ฐ€ํ•ด์คŒ.

๐Ÿฅš์˜ค๋Š˜์˜ ํšŒ๊ณ 

์ดํ‹€ ๋™์•ˆ ์ž ์„ 7์‹œ๊ฐ„ ์žค๋Š”๋ฐ๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์•„์นจ์— ๋ˆˆ์ด ๋ฒˆ์ฉ ๋– ์กŒ๋‹ค.
์ˆ˜๋ฉด ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•ด์„œ ์ˆ˜์—…์‹œ๊ฐ„์— ํ˜น์—ฌ๋‚˜ ์กธ์ง€ ์•Š์„๊นŒ ์‹ถ์—ˆ์ง€๋งŒ,
๋‹คํ–‰ํžˆ ์ˆ˜์—…์€ ์ž˜ ๋”ฐ๋ผ๊ฐ„ ๊ฒƒ ๊ฐ™๋‹ค.
CS ๊ณต๋ถ€๋„ ๋ฌด์ ๊ถŒ ํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ธฐ์—,
์ฒ˜์Œ "HTTP ํ†ต์‹ " ์ด๋ผ๋Š” ๋‹จ์–ด๋ฅผ ๋“ค์—ˆ์„ ๋•Œ
๋‚˜๋„ ๋ฉ˜ํ† ๋‹˜์ด ๋ง์”€ํ•˜์…จ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ๋ญ”๊ฐ€ ๋˜๊ฒŒ ์–ด๋ ค์šธ ๊ฒƒ ๊ฐ™๊ณ 
์‹œ์ž‘ํ•ด๋ณด๊ธฐ๋„ ์ „์— ์กฐ๊ธˆ ๊ฑฑ์ •๋˜๋Š” ๊ฒƒ์ด ์žˆ์—ˆ๋‹ค.
์—ผ๋ ค์™€๋Š” ๋‹ฌ๋ฆฌ ๋‹จ์ง€ ์šฐ๋ฆฌ ๋ˆˆ์— ๋ณด์—ฌ์ง€๊ธฐ๊นŒ์ง€ ์–ด๋– ํ•œ ๊ณผ์ •์œผ๋กœ
์ปดํ“จํ„ฐ ๊ฐ„์— ์†Œํ†ต์„ ํ•˜๋Š”์ง€์— ๊ด€ํ•œ ๋‚ด์šฉ์ด์—ˆ๊ณ 
ํŠนํžˆ ํŽธ์ง€๋ด‰ํˆฌ์— ๋น—๋Œ€์–ด ์ƒ๊ฐ์„ ํ•˜๋‹ˆ ์ดํ•ดํ•˜๊ธฐ๊ฐ€ ๋” ์ˆ˜์›”ํ–ˆ๋‹ค.
ํ™”๋ฉด ๋’ท ๋‹จ์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ์ž‘์—…์ด ์ด๋ค„์ง€๊ณ  ์žˆ์—ˆ๊ตฌ๋‚˜ ํ•˜๋Š” ์ƒ๊ฐ์—
์ƒˆ์‚ผ ๋‚ด๊ฐ€ ํ•˜๋ ค๊ณ  ํ•˜๋Š” ์ผ์ด ๊ฐ„zl๋‚˜๊ฒŒ ๋Š๊ปด์ง€๋Š”,,ใ…‹ใ…‹
์•„๋งˆ ๊ฐˆ์ˆ˜๋ก ๋” ๋ณต์žกํ•˜๊ณ  ์–ด๋ ค์šด ์„ธ๊ณ„๊ฐ€ ํŽผ์ณ์ง€๊ฒ ์ฅ
์ดํ•ด๊ฐ€ ์•ˆ๊ฐ€๋ฉด ๋‘ ๋ฒˆ๋ณด๊ณ  ์„ธ ๋ฒˆ๋ณด๊ณ  ๋‹ค์„ฏ ๋ฒˆ๋„ ๋ณด๊ณ  ํ•˜๋Š” ์ˆ˜๋ฐ–์—!
๊ทธ๋Ÿฌ๋‹ค๋ณด๋ฉด ์Šต๋“๋ ฅ๋„ ์ƒ์Šนํ•˜๊ฒ ์ง€,,?
์–ธ์  ๊ฐ„ ํ•œ ๋ฒˆ ์•Œ๋ ค์ฃผ๋ฉด ๋ฐ”๋กœ ์•„~ํ•˜๊ณ  ์ดํ•ดํ•˜๊ณ  ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋‚ ์ด ์˜ค๊ธฐ๋ฅผ!!

profile
JUST DO WHATEVER

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