Open API๋ ๊ฐ๋ฐฉ๋ ์ธํฐํ์ด์ค๋ก, ๊ฐ๋ฐ์ ๋๊ตฌ๋ ์ ๊ทผํ ์ ์๋ ํํ์ API์ ๋๋ค. ์ฃผ๋ก ๊ธฐ์ ์ด๋ ๊ณต๊ณต๊ธฐ๊ด์ด ์ ๊ณตํ๋ฉฐ, ์ด๋ก์จ ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ ์ด ํน์ ๋ฐ์ดํฐ๋ฅผ ์์ฝ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ํ ์ฑ์์ ๊ธฐ์ ์ ๋ณด๋ฅผ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด ๊ธฐ์์ฒญ์ Open API๋ฅผ ํธ์ถํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ฒ์ด์ฃ . ์ด๋ฅผ ํตํด ๋ค์ํ ์๋น์ค๊ฐ ์๋ก ๋ฐ์ดํฐ๋ฅผ ํตํฉํ๊ฑฐ๋ ๊ธฐ๋ฅ์ ํ์ฅํ ์ ์๋ ๊ธธ์ด ์ด๋ฆฝ๋๋ค! ๐
๋๋ถ๋ถ์ Open API๋ REST(Representational State Transfer) ๋ฐฉ์์ ์ฑํํ๊ณ ์์ต๋๋ค. REST๋ HTTP ํ๋กํ ์ฝ์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฉฐ, ๊ฐ๋จํ๊ณ ํ์คํ๋ ํธ์ถ ๋ฐฉ์์ ์ ๊ณตํ์ฌ ๋ง์ Open API๊ฐ ์ด ๋ฐฉ์์ ๋ฐ๋ฆ ๋๋ค. REST ๋ฐฉ์์ ๊ฐ์ ์ ๊ฐ๋ณ๊ณ ๊ตฌ์กฐ๊ฐ ๋จ์ํ์ฌ ๋ค์ํ ํ๋ซํผ์์ ์ฝ๊ฒ ์ ์ฉํ ์ ์๋ค๋ ์ ์ธ๋ฐ์, ๋๋ถ์ ๊ฐ๋ฐ์๋ค์ ๋ณด๋ค ๋น ๋ฅด๊ณ ๊ฐํธํ๊ฒ Open API๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค. ๐ป๐ฑ
REST(Representational State Transfer)๋ 2000๋ ๋ก์ด ํ๋ฉ์ ๋ ผ๋ฌธ์์ ์ฒ์์ผ๋ก ๋ฑ์ฅํ์ต๋๋ค. HTTP ํ์คํ์ ๊ธฐ์ฌํ ํ๋ฉ์ ์น์ ์ฅ์ ์ ๊ทน๋ํํ ์ ์๋ ์ํคํ ์ฒ ์ค๊ณ๋ก์ REST๋ฅผ ์ ์ํ์ฃ . ์์์ ์ํ๋ฅผ ํด๋ผ์ด์ธํธ์ ์ ๋ฌํ๋ ๋ฐฉ์์ธ REST๋ ํนํ, ํ๋์ URI๊ฐ ๊ณ ์ ํ ๋ฆฌ์์ค๋ฅผ ๋ํํ๋ค๋ ์์น์ ๊ฐ์ง๊ณ ์์ด, ํด๋ผ์ด์ธํธ๊ฐ HTTP ๋ฉ์๋๋ฅผ ํตํด ์์์ ์์ฝ๊ฒ ์ ์ดํ ์ ์๊ฒ ํฉ๋๋ค. ๐
REST์ ๊ฐ์ฅ ํฐ ๋งค๋ ฅ์ ์น์ ํน์ฑ์ ํ์ฉํ ํ์คํ๋ ์ํธ์์ฉ์ ๋๋ค. ๋ถ์ฐ ์์คํ ํ๊ฒฝ์์ REST๋ URI์ HTTP ๋ฉ์๋์ ์กฐํฉ์ ํตํด ์์์ ๋ค๋ฃจ๊ธฐ ๋๋ฌธ์, ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ ์ํธ์์ฉ์ด ์ฌ์์ง๊ณ ์ฝ๋์ ์ ์ง๋ณด์๊ฐ ํธ๋ฆฌํด์ง๋๋ค. ๋ํ, RESTful API๋ ํ์ฅ์ฑ์ด ๋ฐ์ด๋๋ฉฐ, ์ง๊ด์ ์ธ ๊ตฌ์กฐ๋ก ๊ฐ๋ฐ์๋ค์ด ์ฝ๊ฒ ์ ๊ทผํ ์ ์์ต๋๋ค. ์ด๋ก ์ธํด, REST๋ ์น ์๋น์ค API ์ค๊ณ์์ ๊ด๋ฒ์ํ๊ฒ ์ฌ์ฉ๋๊ณ ์์ต๋๋ค. ๐
REST ์ํคํ ์ฒ๋ ์์(Resource), ํ์(Verb), ํํ(Representation)์ด๋ผ๋ ์ธ ๊ฐ์ง ๊ตฌ์ฑ ์์๋ก ์ด๋ฃจ์ด์ ธ ์์ผ๋ฉฐ, ๊ฐ ์์๋ API์ ์ค์ํ ์ญํ ์ ๋ด๋นํฉ๋๋ค. ํ๋์ฉ ์ดํด๋ณผ๊น์? ๐
๊ธฐ์กด ์๋น์ค์ REST ์๋น์ค๋ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ์ญํ ์ ๋๋๋ ๋ฐฉ์์ด ๋ค๋ฆ ๋๋ค. ์๋ฒ๊ฐ ๋ชจ๋ ํ๋ฉด์ ์์ฑํ๋ ๊ธฐ์กด ์๋น์ค์ ๋ฌ๋ฆฌ, REST ์๋น์ค์์๋ ํด๋ผ์ด์ธํธ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ๋ฉด์ ๊ตฌ์ฑํ์ฃ . ์ด ์ฐจ์ด๊ฐ ์ด๋ป๊ฒ ๋์ ๋ฐฉ์๊ณผ ์ ์ฐ์ฑ์์ ์ํฅ์ ๋ฏธ์น๋์ง ์์ธํ ์ดํด๋ณผ๊น์?
ํญ๋ชฉ | ๊ธฐ์กด ์๋น์ค | REST ์๋น์ค |
---|---|---|
์๋ฒ ์ญํ | ๊ฐ ํ๋ซํผ์ ๋ง์ถ View ์์ฑ | ๋ฐ์ดํฐ(JSON/XML)๋ง ๋ฐํ |
ํด๋ผ์ด์ธํธ ์ญํ | ์๋ฒ์์ ์ ๊ณตํ View ์ฌ์ฉ | ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ์ผ๋ก ํ๋ฉด ๊ตฌ์ฑ |
์ ์ง๋ณด์์ฑ | ์๋ฒ์์ ๋ค์ํ View ๊ด๋ฆฌ ํ์ | ํด๋ผ์ด์ธํธ๊ฐ ํ๋ฉด์ ๊ตฌ์ฑํ๋ฏ๋ก ์๋ฒ ๋ถ๋ด ๊ฐ์ |
ํ์ฅ์ฑ | ํ๋ซํผ๋ณ๋ก ๊ฐ๋ณ ๊ด๋ฆฌ ํ์ | ๋ชจ๋ ํด๋ผ์ด์ธํธ์ ๋์ผํ ๋ฐ์ดํฐ ์ ์ก, ํ๋ซํผ ๋ ๋ฆฝ์ฑ ์ ์ง |
REST ์๋น์ค๋ ๋ฐ์ดํฐ๋ง ํด๋ผ์ด์ธํธ์ ์ ๋ฌํ์ฌ ํด๋ผ์ด์ธํธ๊ฐ ํ๋ฉด ๊ตฌ์ฑ์ ์ฑ ์์ง๊ฒ ํจ์ผ๋ก์จ ์๋ฒ์ ๋ถ๋ด์ ์ค์ด๊ณ ๋ค์ํ ํ๋ซํผ์ ์ฝ๊ฒ ๋์ํ ์ ์์ต๋๋ค. Open API์์๋ ์์ฃผ ํ์ฉ๋๋ฉฐ, RESTfulํ ๊ตฌ์กฐ๋ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ ๋์ ๋ ๋ฆฝ์ฑ๊ณผ ํ์คํ๋ ์ ๊ทผ ๋ฐฉ์์ ํตํด ํ๋์ ๋ค์ํ ์น ์๋น์ค์ ์ ๋ฆฌํ ์ ํ์ ๋๋ค. ๐
REST๋ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ๋ฐ์ดํฐ ์ ์ก๊ณผ ์์ ์ ์ด ๋ฐฉ์์ ํ์คํํ์ฌ ์น ์๋น์ค๋ฅผ ์ค๊ณํ ๋ ํจ์จ์ฑ๊ณผ ์ผ๊ด์ฑ์ ์ ๊ณตํฉ๋๋ค. REST์ ์ ์ก ๋ฐฉ์๋ถํฐ URI ์ค๊ณ ๊ท์น๊น์ง ์ฐจ๋ก๋ก ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๊ธฐ์กด์ ์ ์ก ๋ฐฉ์๊ณผ ๋ฌ๋ฆฌ, REST๋ ์์ํ ๋ฐ์ดํฐ๋ง์ ์ ์กํ์ฌ ํ๋ฉด์ ์๋ฒ์์ ์ฒ๋ฆฌํ์ง ์์ต๋๋ค. ์ด๋ ๋ฐ์ดํฐ ์ ์ก์ ํ์คํ๋ ๋ฐฉ์์ผ๋ก ์ํํด ํด๋ผ์ด์ธํธ๊ฐ ์ด๋ฅผ ํ์ฉํ์ฌ ํ๋ฉด์ ๊ตฌ์ฑํ ์ ์๋๋ก ๋์ต๋๋ค. ๐
REST๋ HTTP ๋ฉ์๋๋ฅผ ํตํด ๋ฆฌ์์ค์ CRUD ์์ ์ ๋จ์ํ๊ณ ๋ช ํํ๊ฒ ์ํํฉ๋๋ค:
์ด ๋ฐฉ์์ REST์ ์์ ๊ด๋ฆฌ์ ๋์์ ์ง๊ด์ ์ผ๋ก ๋ถ๋ฆฌํ์ฌ, ํด๋ผ์ด์ธํธ๊ฐ ์ฝ๊ฒ ์ดํดํ๊ณ ์ฌ์ฉํ ์ ์๊ฒ ๋ง๋ญ๋๋ค.
REST๋ URI๋ฅผ ํตํด ์์์ ๋ช ํํ ์๋ณํ๋ฉฐ, HTTP ๋ฉ์๋๋ฅผ ํตํด ์ด ์์์ ๋ํ ๋์์ ์ ์ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ํน์ ์์์ ๋ํ๋ด๋ URI์ ํจ๊ป GET, POST ๋ฑ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ์์์ ์ ๊ทผํ๊ฑฐ๋ ์์ ํ ์ ์์ต๋๋ค. ์ด๋ ์์์ URL๋ก ๊ฐ๋ฆฌํค๊ณ HTTP ๋ฉ์๋๋ก ์กฐ์ํ๋ ์ํคํ ์ฒ๋ก, ๋ฐ์ดํฐ ์ ์ก๊ณผ ์กฐ์์ ํ์คํ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค. ๐
REST์์๋ ์์์ ํํ ๋ฐฉ์์ Collection(๋ฐ์ดํฐ์ ์งํฉ)๊ณผ Document(๊ฐ๋ณ ๋ฐ์ดํฐ)๋ก ๊ตฌ๋ถํฉ๋๋ค. ์๋ฅผ ๋ค์ด:
http://www.example.com/sports/baseball/players/31
์์ Collection์ "sports", "baseball", "players"์ด๊ณ ,์ด๋ฌํ ๊ณ์ธต ๊ตฌ์กฐ๋ ๋ฐ์ดํฐ ์ ๊ทผ์ ์ฒด๊ณ์ ์ด๊ณ ์ผ๊ด๋๊ฒ ํ ์ ์๋๋ก ์ค๊ณ๋ฉ๋๋ค. ๐๏ธ
REST์์๋ ํจ์จ์ ์ธ URI ์ค๊ณ๋ฅผ ์ํด ์๋ฌต์ ์ธ ๊ท์น์ ๋ฐ๋ฆ ๋๋ค:
๊ธฐ์กด ์น ์ ๊ทผ ๋ฐฉ์๊ณผ REST API ๋ฐฉ์์ CRUD ์์ ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์์ ํฐ ์ฐจ์ด๋ฅผ ๋ณด์ ๋๋ค. ํนํ HTTP ๋ฉ์๋์ URI ์ค๊ณ ๊ตฌ์กฐ์์ ๋๋๋ฌ์ง ์ฐจ์ด๊ฐ ๋ํ๋๋ฉฐ, REST ๋ฐฉ์์ ๋ณด๋ค ํ์คํ๋๊ณ ์ผ๊ด์ฑ ์๋ ๋ฐฉ์์ผ๋ก ์ ๊ทผํฉ๋๋ค. ํ๋์ฉ ๋น๊ตํด๋ณด๊ฒ ์ต๋๋ค.
action=write
, action=view
๋ฑ์ ํ๋ผ๋ฏธํฐ๋ก ๊ฐ ์์
์ ์ ์ํฉ๋๋ค./blog?action=write&id=tromment
โ action=write
๋ก ๊ธ์ฐ๊ธฐ ์์
์ ๋ช
์ํฉ๋๋ค./blog?action=view&id=tromment&articleno=25
โ action=view
๋ก ๊ธ ์กฐํ ์์
์ ์ํํ๋ฉฐ, id
์ articleno
๋ก ํน์ ๊ธ์ ์กฐํํฉ๋๋ค./blog?action=modify&id=tromment
โ action=modify
๋ฅผ ํตํด ๊ธ ์์ ์์
์ ๋ช
์ํฉ๋๋ค./blog?action=delete&id=tromment&articleno=25
โ action=delete
๋ก ๊ธ ์ญ์ ์์
์ ์ํํฉ๋๋ค./blog/tromment
โ POST ๋ฉ์๋๋ฅผ ํตํด ๊ธ์ฐ๊ธฐ๋ฅผ ์ํํ๋ฉฐ, URI๋ก ๋ฆฌ์์ค๋ฅผ ์์ฑํ๋ ์์
์ ๋ช
ํํ ์ ์ํฉ๋๋ค./blog/tromment/25
โ GET ๋ฉ์๋๋ฅผ ํตํด ํน์ ๊ธ(tromment/25
)์ ์กฐํํ๋ ์์
์ ์ํํฉ๋๋ค./blog/tromment
โ PUT ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๊ธ์ ์์ ํ๋ฉฐ, URI๋ ์์ ํ ๋์ ๋ฆฌ์์ค๋ฅผ ๋ํ๋
๋๋ค./blog/tromment/25
โ DELETE ๋ฉ์๋๋ฅผ ํตํด ํน์ ๊ธ(tromment/25
)์ ์ญ์ ํฉ๋๋ค.ํญ๋ชฉ | ๊ธฐ์กด ๋ฐฉ์ | REST ๋ฐฉ์ |
---|---|---|
HTTP ๋ฉ์๋ | ์ฃผ๋ก GET๊ณผ POST ์ฌ์ฉ | GET, POST, PUT, DELETE ๋ฑ ์์ ์ ๋ง๋ ๋ฉ์๋ ์ฌ์ฉ |
URI ๊ตฌ์กฐ | action ํ๋ผ๋ฏธํฐ๋ก ์์
๊ตฌ๋ถ | ๋ช ์ฌํ URI๋ก ๋ฆฌ์์ค๋ฅผ ์ง์นญํ๊ณ , HTTP ๋ฉ์๋๋ก ์์ ๊ตฌ๋ถ |
์ ์ง๋ณด์์ฑ | ์ฟผ๋ฆฌ์คํธ๋ง์ผ๋ก ๋ค์ํ ์์ ์ ์ฒ๋ฆฌํด ์ง๊ด์ฑ์ด ๋ฎ์ | ์์ ๊ณผ ๋ฆฌ์์ค๊ฐ ๋ถ๋ฆฌ๋์ด ์ผ๊ด์ฑ, ๊ฐ๋ ์ฑ, ์ ์ง๋ณด์์ฑ์ด ๋ฐ์ด๋จ |
REST ๋ฐฉ์์ HTTP ๋ฉ์๋์ URI ์ค๊ณ๋ฅผ ํ์คํํ์ฌ ์๋น์ค์ ์ผ๊ด์ฑ์ ๋์ด๊ณ , ์ ์ง๋ณด์์ฑ๊ณผ ๊ฐ๋ ์ฑ์ ํฅ์์ํต๋๋ค. ํนํ, Open API์ ๊ฐ์ ํ๊ฒฝ์์ RESTful ์ค๊ณ๋ ๋ค์ํ ํด๋ผ์ด์ธํธ์์ ์ฐ๋์ ๋ณด๋ค ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด ํ๋ ์น ์๋น์ค์ ํ์ ์์๋ก ์๋ฆฌ ์ก์์ต๋๋ค. ๐
Jackson ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์๋ฐ ๊ฐ์ฒด๋ฅผ JSON์ด๋ XML ํ์์ผ๋ก ๋ณํํ์ฌ REST API์์ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ๋์์ฃผ๋ ํ์ ๋๊ตฌ์ ๋๋ค. ๋ฐ์ดํฐ๋ฅผ ์ง๋ ฌํํ๊ฑฐ๋ ์ญ์ง๋ ฌํํ์ฌ, ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ ์ํํ ๋ฐ์ดํฐ ์ ์ก์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
jackson-databind: JSON ๋ณํ์ ํ์์ ์ธ ๋ชจ๋
jackson-dataformat-xml: XML ๋ณํ์ ์ํ ๋ชจ๋
Maven ํ๋ก์ ํธ์ Jackson ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ ค๋ฉด pom.xml ํ์ผ์ Jackson ์์กด์ฑ์ ์ค์ ํด์ผ ํฉ๋๋ค. ์๋์ ๊ฐ์ด jackson-databind ๋ชจ๋์ ์ถ๊ฐํ์ฌ JSON ๋ณํ์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind-version}</version>
</dependency>
Tip:
${jackson-databind-version}
์ ์ต์ ๋ฒ์ ๋ฒํธ๋ก ๋์ฒดํ์ฌ Jackson ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ค๋ฉด ๋ฉ๋๋ค!
REST API ๊ตฌํ ์ ํ์์ ์ธ ์ด๋ ธํ ์ด์ ๋ค์ ๋ฐ์ดํฐ ์ ์ก, URL ๊ฒฝ๋ก ์ฒ๋ฆฌ, ํฌ๋ก์ค ๋๋ฉ์ธ ํ์ฉ ๋ฑ ์ฌ๋ฌ ๊ธฐ๋ฅ์ ๊ฐํธํ๊ฒ ๊ตฌํํ ์ ์๊ฒ ํฉ๋๋ค. ๊ฐ๊ฐ์ ์ด๋ ธํ ์ด์ ์ ํตํด RESTful API์ ์ฃผ์ ๊ธฐ๋ฅ์ ์์ฝ๊ฒ ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
@RestController
๋ ํด๋น ํด๋์ค๊ฐ REST ๋ฐฉ์์ ์ฒ๋ฆฌํ๋ ์ปจํธ๋กค๋ฌ์์ ๋ํ๋
๋๋ค.@Controller
์ @ResponseBody
๊ธฐ๋ฅ์ ๊ฒฐํฉํ์ฌ, ํด๋์ค ๋ด ๋ชจ๋ ๋ฉ์๋๊ฐ JSON ๋๋ XML ํํ์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๋ฐํํ๊ฒ ๋ง๋ญ๋๋ค.@RestController
๊ฐ ์ ์ธ๋ ํด๋์ค์๋ ์๋์ผ๋ก ํฌํจ๋ฉ๋๋ค. ๐ฌ/users/{id}
๊ฒฝ๋ก์ {id}
๋ฅผ ์ธ์๋ก ๋ฐ์์ฌ ์ ์์ต๋๋ค.http://example.com
์์ http://api.example.com
์ ์์ฒญํ ๋ ์ด๋ฅผ ํ์ฉํฉ๋๋ค. ๐์ด๋ ธํ ์ด์ | ๊ธฐ๋ฅ | ์ฉ๋ |
---|---|---|
@RestController | RESTful API ์ปจํธ๋กค๋ฌ๋ก ์ ์ธ | ๋ฐ์ดํฐ๋ฅผ ์ง์ ๋ฐํํ ๋ |
@ResponseBody | ๋ฐ์ดํฐ๋ฅผ JSON, XML ํ์์ผ๋ก ์ง์ ์๋ต | JSON, XML ์๋ต์ ํ์๋ก ํ ๋ |
@PathVariable | URL ๊ฒฝ๋ก์ ํฌํจ๋ ๋ณ์๋ฅผ ์ถ์ถ | ํน์ ์์ ์ ๊ทผ์ ์ํ ๋ณ์ ๋ฐ์ธ๋ฉ |
@CrossOrigin | ํฌ๋ก์ค ๋๋ฉ์ธ ์์ฒญ์ ํ์ฉ | ๋ค๋ฅธ ๋๋ฉ์ธ์ ์์ฒญ์ ํ์ฉํ ๋ |
@RequestBody | JSON ๋ฐ์ดํฐ๋ฅผ ์๋ฐ ๊ฐ์ฒด๋ก ๋ณํ | JSON ํ์์ ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉํ ๋ |
์ด ์ด๋ ธํ ์ด์ ๋ค์ REST API์์ ๋ค์ํ ๋ฐ์ดํฐ ์ ์ก๊ณผ ์์ฒญ ์ฒ๋ฆฌ๋ฅผ ๊ฐํธํ๊ฒ ๋ง๋ค์ด, RESTful ๋ฐฉ์์ API ๊ฐ๋ฐ์ ํจ์จ์ ์ด๊ณ ์ผ๊ด์ฑ ์๊ฒ ํ ์ ์์ต๋๋ค! ๐๐ฆ
์๋ ํ์ธ์, ๊ฐ๋ฐ์ ์ฌ๋ฌ๋ถ! ์ค๋์ Java์ JavaScript๋ฅผ ํ์ฉํ์ฌ RESTful API๋ก ํ์ ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค. ํฅ๋ฏธ์ง์งํ ๋ด์ฉ์ด ๊ฐ๋ํ๋ ๋๊น์ง ํจ๊ปํด ์ฃผ์ธ์! ๐ช
ํ์ ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๋ GET
์๋ํฌ์ธํธ๋ฅผ ์์ฑํฉ๋๋ค.
@GetMapping("/users")
public ResponseEntity<List<UserDto>> getAllUsers() {
try {
List<UserDto> users = userService.getAllUsers();
if (users.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<>(users, HttpStatus.OK);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/users
๊ฒฝ๋ก๋ก ๋ค์ด์ค๋ GET
์์ฒญ์ ์ฒ๋ฆฌํ์ฌ ๋ชจ๋ ํ์ ๋ชฉ๋ก์ ๋ฐํํฉ๋๋ค.NO_CONTENT
์ํ๋ฅผ ๋ฐํํฉ๋๋ค.INTERNAL_SERVER_ERROR
์ํ๋ฅผ ๋ฐํํฉ๋๋ค.์๋ฒ๋ก๋ถํฐ ํ์ ๋ชฉ๋ก์ ๋ฐ์์ ํ๋ฉด์ ํ์ํฉ๋๋ค.
fetch('/api/users')
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(users => renderUserList(users))
.catch(error => console.error('Error fetching user list:', error));
fetch
๋ฅผ ์ฌ์ฉํ์ฌ /api/users
์๋ํฌ์ธํธ์ GET
์์ฒญ์ ๋ณด๋
๋๋ค.renderUserList
ํจ์๋ฅผ ํธ์ถํ์ฌ ํ๋ฉด์ ๋ ๋๋งํฉ๋๋ค.์๋ก์ด ํ์์ ๋ฑ๋กํ๋ POST
์๋ํฌ์ธํธ๋ฅผ ์์ฑํฉ๋๋ค.
@PostMapping("/users")
public ResponseEntity<UserDto> registerUser(@RequestBody UserDto userDto) {
try {
UserDto createdUser = userService.registerUser(userDto);
return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/users
๊ฒฝ๋ก๋ก ๋ค์ด์ค๋ POST
์์ฒญ์ ์ฒ๋ฆฌํ์ฌ ์๋ก์ด ํ์์ ๋ฑ๋กํฉ๋๋ค.UserDto
๊ฐ์ฒด๋ฅผ ์ฌ์ฉํฉ๋๋ค.CREATED
์ํ์ ํจ๊ป ์์ฑ๋ ํ์ ์ ๋ณด๋ฅผ ๋ฐํํฉ๋๋ค.์ฌ์ฉ์ ์
๋ ฅ์ ๋ฐ์ ์๋ฒ๋ก POST
์์ฒญ์ ๋ณด๋
๋๋ค.
document.getElementById('registerButton').addEventListener('click', () => {
const userData = {
username: document.getElementById('username').value,
password: document.getElementById('password').value,
email: document.getElementById('email').value,
};
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
})
.then(response => {
if (!response.ok) throw new Error('Failed to register user');
return response.json();
})
.then(newUser => {
console.log('User registered:', newUser);
// ์ถ๊ฐ ์์
: ํ์ ๋ชฉ๋ก ๊ฐฑ์ , ์ฑ๊ณต ๋ฉ์์ง ํ์ ๋ฑ
})
.catch(error => console.error('Error registering user:', error));
});
Content-Type
์ application/json
์ผ๋ก ์ค์ ํฉ๋๋ค.ํน์ ํ์์ ์ ๋ณด๋ฅผ ๋ฐํํ๋ GET
์๋ํฌ์ธํธ๋ฅผ ์์ฑํฉ๋๋ค.
@GetMapping("/users/{id}")
public ResponseEntity<UserDto> getUserById(@PathVariable Long id) {
try {
Optional<UserDto> user = userService.getUserById(id);
return user.map(u -> new ResponseEntity<>(u, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/users/{id}
๊ฒฝ๋ก๋ก ๋ค์ด์ค๋ GET
์์ฒญ์ ์ฒ๋ฆฌํ์ฌ ํน์ ํ์์ ์ ๋ณด๋ฅผ ๋ฐํํฉ๋๋ค.NOT_FOUND
์ํ๋ฅผ ๋ฐํํฉ๋๋ค.ํ์ ์ ๋ณด๋ฅผ ์์ฒญํ์ฌ ํ๋ฉด์ ํ์ํฉ๋๋ค.
function viewUserDetails(userId) {
fetch(`/api/users/${userId}`)
.then(response => {
if (!response.ok) throw new Error('User not found');
return response.json();
})
.then(user => {
// ์ฌ์ฉ์ ์์ธ ์ ๋ณด๋ฅผ ํ๋ฉด์ ํ์
document.getElementById('userDetail').innerText = `
ID: ${user.id}
์ด๋ฆ: ${user.username}
์ด๋ฉ์ผ: ${user.email}
`;
})
.catch(error => console.error('Error fetching user details:', error));
}
ํ์ ์ ๋ณด๋ฅผ ์
๋ฐ์ดํธํ๋ PUT
์๋ํฌ์ธํธ๋ฅผ ์์ฑํฉ๋๋ค.
@PutMapping("/users/{id}")
public ResponseEntity<UserDto> updateUser(@PathVariable Long id, @RequestBody UserDto userDto) {
try {
Optional<UserDto> updatedUser = userService.updateUser(id, userDto);
return updatedUser.map(u -> new ResponseEntity<>(u, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/users/{id}
๊ฒฝ๋ก๋ก ๋ค์ด์ค๋ PUT
์์ฒญ์ ์ฒ๋ฆฌํ์ฌ ํ์ ์ ๋ณด๋ฅผ ์์ ํฉ๋๋ค.NOT_FOUND
์ํ๋ฅผ ๋ฐํํฉ๋๋ค.์์ ๋ ์ ๋ณด๋ฅผ ์๋ฒ๋ก ์ ์กํฉ๋๋ค.
function updateUser(userId) {
const updatedData = {
username: document.getElementById(`username_${userId}`).value,
email: document.getElementById(`email_${userId}`).value,
};
fetch(`/api/users/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(updatedData),
})
.then(response => {
if (!response.ok) throw new Error('Failed to update user');
return response.json();
})
.then(user => {
console.log('User updated:', user);
// ์ถ๊ฐ ์์
: ํ๋ฉด ๊ฐฑ์ , ์ฑ๊ณต ๋ฉ์์ง ํ์ ๋ฑ
})
.catch(error => console.error('Error updating user:', error));
}
PUT
์์ฒญ์ ๋ณด๋
๋๋ค.ํ์ ์ญ์ ๋ฅผ ์ฒ๋ฆฌํ๋ DELETE
์๋ํฌ์ธํธ๋ฅผ ์์ฑํฉ๋๋ค.
@DeleteMapping("/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
try {
boolean isDeleted = userService.deleteUser(id);
if (isDeleted) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
/users/{id}
๊ฒฝ๋ก๋ก ๋ค์ด์ค๋ DELETE
์์ฒญ์ ์ฒ๋ฆฌํ์ฌ ํ์์ ์ญ์ ํฉ๋๋ค.NO_CONTENT
์ํ๋ฅผ ๋ฐํํฉ๋๋ค.ํ์ ์ญ์ ์์ฒญ์ ์๋ฒ๋ก ๋ณด๋ ๋๋ค.
function deleteUser(userId) {
if (confirm('์ ๋ง๋ก ์ด ํ์์ ์ญ์ ํ์๊ฒ ์ต๋๊น?')) {
fetch(`/api/users/${userId}`, {
method: 'DELETE',
})
.then(response => {
if (!response.ok) throw new Error('Failed to delete user');
console.log('User deleted');
// ์ถ๊ฐ ์์
: ํ์ ๋ชฉ๋ก ๊ฐฑ์ ๋ฑ
})
.catch(error => console.error('Error deleting user:', error));
}
}
DELETE
์์ฒญ์ ๋ณด๋
๋๋ค.ํ์ ๋ชฉ๋ก์ ๋์ ์ผ๋ก ์์ฑํ์ฌ ํ ์ด๋ธ์ ํ์ํฉ๋๋ค.
function renderUserList(users) {
const tbody = document.getElementById('userTableBody');
tbody.innerHTML = ''; // ๊ธฐ์กด ๋ด์ฉ์ ์ง์๋๋ค.
users.forEach(user => {
const row = document.createElement('tr');
// ID ์
const idCell = document.createElement('td');
idCell.textContent = user.id;
row.appendChild(idCell);
// ์ด๋ฆ ์
const nameCell = document.createElement('td');
nameCell.textContent = user.username;
row.appendChild(nameCell);
// ์ด๋ฉ์ผ ์
const emailCell = document.createElement('td');
emailCell.textContent = user.email;
row.appendChild(emailCell);
// ์ก์
์
const actionCell = document.createElement('td');
const viewButton = document.createElement('button');
viewButton.textContent = '๋ณด๊ธฐ';
viewButton.onclick = () => viewUserDetails(user.id);
actionCell.appendChild(viewButton);
const editButton = document.createElement('button');
editButton.textContent = '์์ ';
editButton.onclick = () => showEditForm(user.id);
actionCell.appendChild(editButton);
const deleteButton = document.createElement('button');
deleteButton.textContent = '์ญ์ ';
deleteButton.onclick = () => deleteUser(user.id);
actionCell.appendChild(deleteButton);
row.appendChild(actionCell);
tbody.appendChild(row);
});
}
์ฌ๊ธฐ๊น์ง Java์ JavaScript๋ฅผ ํ์ฉํ์ฌ RESTful API๋ก ํ์ ๊ด๋ฆฌ ์์คํ ์ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์์ต๋๋ค. ์ด๋ฒ ๊ธ์ ํตํด ๋ค์์ ์ดํดํ์ จ์ ๊ฒ๋๋ค.
์ด๋ฌํ ์ง์์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ฉด ํจ์จ์ ์ด๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ์ ์์ ๊ฒ์ ๋๋ค. ์์ผ๋ก๋ ๊พธ์คํ ํ์ต๊ณผ ์ค์ ๊ฒฝํ์ ํตํด ๋์ฑ ์ฑ์ฅํ๋ ๊ฐ๋ฐ์๊ฐ ๋์๊ธธ ๋ฐ๋๋๋ค! ๐
์ถ๊ฐ ํ ๐ก
๊ถ๊ธํ ์ ์ด๋ ๋์์ด ํ์ํ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ๋๊ธ๋ก ๋จ๊ฒจ์ฃผ์ธ์! ํจ๊ป ์ฑ์ฅํด์! ๐
ResponseEntity<T>๋ ์คํ๋ง ํ๋ ์์ํฌ์์ ์ ๊ณตํ๋ ํด๋์ค๋ก, HTTP ์๋ต์ ์ํ ์ฝ๋, ์๋ต ํค๋, ๋ณธ๋ฌธ ๋ฐ์ดํฐ๋ก ๊ตฌ์ฑํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ ์ ์๊ฒ ํด์ค๋๋ค. REST API์์ ์ฌ์ฉํ๋ฉด ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ๊ตฌ์ฒด์ ์ด๊ณ ๊ตฌ์กฐํ๋ ์๋ต์ ์ ๊ณตํ ์ ์์ต๋๋ค. ์ด ํด๋์ค๊ฐ ์๋ต์ ์ธ๋ถ์ ์ธ ์์๋ฅผ ์ด๋ป๊ฒ ๊ตฌ์ฑํ๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค!
์ํ ์ฝ๋ (HTTP Status Code):
200 OK
, 404 NOT FOUND
, 500 INTERNAL SERVER ERROR
๋ฑ์ HTTP ์ํ ์ฝ๋๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.์๋ต ํค๋ (Response Header):
Content-Type
, Cache-Control
, Authorization
๋ฑ ํด๋ผ์ด์ธํธ๊ฐ ์๋ต์ ์ฒ๋ฆฌํ๋ ๋ฐ ํ์ํ ๋ถ๊ฐ ์ ๋ณด๋ฅผ ์ ๊ณตํฉ๋๋ค.๋ณธ๋ฌธ ๋ฐ์ดํฐ (Response Body):
ResponseEntity
์ ์ ๋ค๋ฆญ ํ์
<T>
๋ฅผ ํตํด ๋ณธ๋ฌธ ๋ฐ์ดํฐ์ ํ์
์ ์ ์ฐํ๊ฒ ์ค์ ํ ์ ์์ต๋๋ค. ๐ฆResponseEntity<T>
์์ T๋ ์๋ต ๋ณธ๋ฌธ ๋ฐ์ดํฐ์ ํ์
์ ์ ์ํ๋ฉฐ, ๋ค์ํ ์ํฉ์ ๋ง์ถฐ ์ค์ ํ ์ ์์ต๋๋ค:
1. ๋ฐ์ดํฐ ๋ฐํ ์์ - ์ฑ๊ณต์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ ๊ฒฝ์ฐ:
public ResponseEntity<List<MemberDto>> getMembers() {
List<MemberDto> members = memberService.getAllMembers();
return new ResponseEntity<>(members, HttpStatus.OK); // ์ํ ์ฝ๋ 200๊ณผ ํจ๊ป ๋ฐ์ดํฐ ๋ฐํ
}
2. ๋ฐ์ดํฐ๊ฐ ์์ ๊ฒฝ์ฐ - NO_CONTENT๋ก ์๋ต:
public ResponseEntity<Void> deleteMember(String id) {
boolean deleted = memberService.deleteById(id);
if (deleted) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT); // ์ํ ์ฝ๋ 204, ๋ณธ๋ฌธ ์์
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND); // ์ํ ์ฝ๋ 404 ๋ฐํ
}
}
ResponseEntity<T>
ํด๋์ค๋ REST API ์๋ต์ ์ํ ์ฝ๋, ๋ณธ๋ฌธ, ํค๋ ๋ฑ์ ์ธ๋ฐํ๊ฒ ์ค์ ํ์ฌ, ํด๋ผ์ด์ธํธ์๊ฒ ๋ช
ํํ๊ณ ๊ตฌ์กฐํ๋ ์๋ต์ ์ ๋ฌํ ์ ์๋๋ก ํฉ๋๋ค. ์ด๋ฌํ ์์๋ณ๋ก ๊ฐ๊ฐ์ ๊ธฐ๋ฅ์ ์ด๋ป๊ฒ ์ค์ ํ๊ณ ํ์ฉํ๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค!
ResponseEntity<String> response = new ResponseEntity<>(HttpStatus.OK);
HttpStatus.OK
๋ ์ํ ์ฝ๋ 200์ ์๋ฏธํ๋ฉฐ, ์ด๋ ์์ฒญ์ด ์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌ๋์์์ ๋ํ๋
๋๋ค.ResponseEntity<List<MemberDto>> response = new ResponseEntity<>(list, HttpStatus.OK);
list
๋ JSON ํ์์ผ๋ก ๋ณํ๋์ด ์๋ต ๋ณธ๋ฌธ์ ํฌํจ๋ฉ๋๋ค. HttpStatus.OK
๋ ์ํ ์ฝ๋ 200์ ๋ํ๋
๋๋ค.<T>
๋ฅผ ํตํด ๋ค์ํ ํ์
์ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ ์ ์์ด, ์๋ต์ ์ ์ฐ์ฑ์ ๋์
๋๋ค. ๐Content-Type
, Location
๋ฑ์ด ์์ผ๋ฉฐ, ์ถ๊ฐ์ ์ธ ์ ๋ณด๊ฐ ํ์ํ ๊ฒฝ์ฐ ์ปค์คํ
ํค๋๋ ์ค์ ํ ์ ์์ต๋๋ค.HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "HeaderValue");
ResponseEntity<String> response = new ResponseEntity<>("Body Content", headers, HttpStatus.OK);
Custom-Header
๋ผ๋ ์ปค์คํ
ํค๋๊ฐ ์ค์ ๋ ์ํ๋ก "Body Content"
๋ฅผ ๋ณธ๋ฌธ์ผ๋ก, ์ํ ์ฝ๋ 200์ ํฌํจํ์ฌ ์๋ต์ ๋ฐํํฉ๋๋ค.application/json
ResponseEntity<T>
๋ ๋ค์ํ ์ ์ ๋ฉ์๋๋ฅผ ํตํด ์ํ ์ฝ๋์ ๋ณธ๋ฌธ, ํค๋ ๋ฑ์ ๊ฐํธํ๊ฒ ์ค์ ํ ์ ์์ด REST API ์๋ต์ ํจ์จ์ ์ด๊ณ ๋ช
ํํ๊ฒ ๊ตฌ์ฑํ ์ ์์ต๋๋ค. ์ด๋ฌํ ์์ฑ ๋ฐฉ์์ ํตํด ์ฝ๋์ ๊ฐ๋
์ฑ์ ๋์ด๊ณ ์ํฉ์ ๋ง๋ ์๋ต์ ์์ฝ๊ฒ ์์ฑํ ์ ์์ต๋๋ค.
ResponseEntity
๊ฐ์ฒด๋ new
ํค์๋๋ฅผ ์ฌ์ฉํด ์ง์ ์์ฑํ ์ ์์ต๋๋ค.new ResponseEntity<>(list, HttpStatus.OK);
ResponseEntity
์ ์ ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด, ๋ ๊ฐ๊ฒฐํ๊ฒ ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.ResponseEntity.ok()
๋ฉ์๋๋ ์ํ ์ฝ๋ 200(OK)์ ์ค์ ํ๋ฉฐ, ์๋ต ๋ณธ๋ฌธ์ ํฌํจํ ์ ์์ต๋๋ค.return ResponseEntity.ok(list);
HttpStatus.OK
์ํ ์ฝ๋์ ํจ๊ป list
๊ฐ์ฒด๋ฅผ ๋ณธ๋ฌธ์ผ๋ก ํฌํจํ์ฌ ๋ฐํํฉ๋๋ค. ๊ฐ๊ฒฐํ ์ฑ๊ณต ์๋ต์ ์ํ ์ต์ ์ ๋ฐฉ๋ฒ์
๋๋ค.ResponseEntity.status()
๋ฉ์๋๋ ์ํ ์ฝ๋๋ฅผ ์ค์ ํ ํ, ์ฒด์ด๋ ๋ฐฉ์์ผ๋ก body()
๋ฉ์๋๋ฅผ ํตํด ๋ณธ๋ฌธ์ ์ถ๊ฐํ ์ ์์ต๋๋ค.return ResponseEntity.status(HttpStatus.CREATED).body("Created Resource");
"Created Resource"
๋ผ๋ ๋ณธ๋ฌธ์ ์ค์ ํ์ฌ ๋ฐํํฉ๋๋ค. ์ฃผ๋ก ๋ฆฌ์์ค ์์ฑ ์ฑ๊ณต์ ๋ํ๋ผ ๋ ์ฌ์ฉ๋ฉ๋๋ค.ResponseEntity.noContent()
๋ฉ์๋๋ ์ํ ์ฝ๋ 204(No Content)๋ฅผ ์ค์ ํ๋ฉฐ, ๋ณธ๋ฌธ ์์ด ์๋ต์ ๋ณด๋ผ ๋ ์ฌ์ฉ๋ฉ๋๋ค.return ResponseEntity.noContent().build();
ok()
, status()
, noContent()
)๋ฅผ ํตํด ์๋ต์ ์ํ ์ฝ๋์ ๋ณธ๋ฌธ์ ์ง๊ด์ ์ผ๋ก ์ค์ ํ ์ ์์ต๋๋ค.ResponseEntity
์ ์ ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๋์ ๊ฐ๋
์ฑ์ ๋์ด๊ณ , ์๋ต ๊ตฌ์ฑ์ ๊ฐ๊ฒฐํ๊ฒ ๋ง๋ค์ด RESTful API์ ์๋ต์ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์ต๋๋ค. ๐