๊ฐ๋ฐ์ ํต์ฌ ์ค ํ๋๋ ๋ถ์ ๊ณผ ๋์จํ ๊ฒฐํฉ์ด๋ค. ๋ถ์ ์ ํตํด ๊ธฐ๋ฅ์ ์จ์ ํ ์๋ํ๊ฒ ํ๊ณ , ๋์จํ ๊ฒฐํฉํจ์ผ๋ก์จ ์ ์ฐ์ฑ๊ณผ ํ์ฅ์ฑ์ ๊ฐ์ง ์ ์๋ค. ์คํ๋ง์ ์ฒ์ถ๋ผ ํ ์ ์๋ Controller, Service, Repository 3๊ณ์ธต์ ๋ถ์ ๊ณผ ๋์จํ ๊ฒฐํฉ์ ๋ํ์ ์ธ ์์์ด๋ค.
๋์จํ ๊ฒฐํฉ์ ๋ ๋ค๋ฅธ ์์๊ฐ ๋ฐ๋ก API์ด๋ค. ๋ด๊ฐ API์ ๋ด๋ถ ๋ก์ง์ด ์ด๋ป๊ฒ ์ง์ฌ์ก๋์ง ๋ชจ๋ฅด๋ ์ํฉ์์๋, ๊ณต์ ๋ฌธ์์ ๋์จ ์ ํด์ง ์ฝ์๋๋ก ์๊ตฌ๋ฅผ ํ๋ฉด ์ ํด์ง ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ์ ์๋ค. ๋ฐ๋ผ์ API๋ฅผ "์ฌ์ฉ"ํ ์ค ์๋ ๊ฒ์ด ์ค์ํ๋ค.
์ด๋ฒ ์ฃผ์ฐจ ์์ ๋ด์ฉ์ ์คํ๋ง์ 3๊ณ์ธต๊ณผ API handling ์ฐ์ต์ ์ค์ ์ ๋๋ค. ์ด๋ฒ์ ๋ง๋ค ๊ฒ์ <๋๋ง์ ์ ๋ ์ต>์ด๋ค. ์๊ตฌ ๊ธฐ๋ฅ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ํ๋ช ์ ๋ฐ๋ฅธ ๊ฒ์
- ๊ด์ฌ ์ํ ๋ฑ๋ก & ์กฐํ
- ๊ด์ฌ ์ํ์ ๋ํ ์ต์ ๊ฐ ๋ฑ๋กํ๊ธฐ
์๋ ์ฃผ์์์ ๋ฐ๋ชจ๋ฅผ ํ์ธํ ์ ์๋ค.
http://spring.spartacodingclub.kr/
ํ๋ก์ ํธ ์ค๊ณํ๊ธฐ
๐ก API ์ค๊ณํ๊ธฐ
์!!!! ์ด์ API๋ฅผ ์ค๊ณํ ์๊ฐ์ด๋ค. ์ฒ์์ ๋๋ ์ํ๋ช ์ ๋ฐ๋ฅธ ๊ฒ์๊ณผ ๊ด์ฌ ์ํ ๋ฑ๋ก์ url์ ๊ตฌ๋ถํ์ง ์๊ณ ํ๋ คํด์ ๋ณต์กํด์ง ๋ป ํ๋๋ฐ, ๊ฐ์๋ฅผ ๋ณด๋ ๋ค์๊ณผ ๊ฐ์ด ๊น๋ํ๊ฒ ์ ๋ฆฌํ ์ ์์๋ค.
๊ธฐ๋ฅ | Method | URL | return |
ํค์๋๋ก ์ํ ๊ฒ์ํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ชฉ๋ก์ผ๋ก ๋ณด์ฌ์ฃผ๊ธฐ | GET | /api/search?query=๊ฒ์์ด | List<ItemDto> |
๊ด์ฌ ์ํ ๋ฑ๋กํ๊ธฐ | POST | /api/products | Product |
๊ด์ฌ ์ํ ์กฐํํ๊ธฐ | GET | /api/products | List<Product> |
๊ด์ฌ ์ํ์ ๊ด์ฌ ๊ฐ๊ฒฉ ๋ฑ๋กํ๊ณ , ์ํ ๊ฐ๊ฒฉ์ด ๋ ๋ฎ์ ๊ฒฝ์ฐ ํ์ํ๊ธฐ | PUT | /api/products/{id} | id |
๐ก 3๊ณ์ธต ์ค๊ณํ๊ธฐ
1. Controller
- ProductRestController: ๊ด์ฌ ์ํ ๊ด๋ จ ์ปจํธ๋กค๋ฌ
- SearchRequestContorller: ๊ฒ์ ๊ด๋ จ ์ปจํธ๋กค๋ฌ
2. Service
- ProductService: ๊ด์ฌ ์ํ ๊ฐ๊ฒฉ ๋ณ๊ฒฝ
3. Repository
- Product: ๊ด์ฌ ์ํ ํ ์ด๋ธ
- ProductRepository: ๊ด์ฌ ์ํ ์กฐํ, ์ ์ฅ
- ProductRequestDto: ๊ด์ฌ ์ํ ๋ฑ๋กํ๊ธฐ
- ProductMypriceRequestDto: ๊ด์ฌ ๊ฐ๊ฒฉ ๋ณ๊ฒฝํ๊ธฐ
- ItemDto: ๊ฒ์ ๊ฒฐ๊ณผ ์ฃผ๊ณ ๋ฐ๊ธฐ (DB์ ์ ์ฅํ ํ์X)
์ด์ "์ํ๋ช ์ ๋ฐ๋ฅธ ๊ฒ์" ๊ธฐ๋ฅ์ ๋ง๋ค๊ธฐ ์ํด ๋ค์ด๋ฒ ์ผํ API๋ฅผ ์ด์ฉํด๋ณด์~~
๋ค์ด๋ฒ ์ผํ API ์ด์ฉํ๊ธฐ
๐ก API ์ด์ฉ ์ ์ฒญํ๊ธฐ
- https://developers.naver.com/docs/search/shopping/์ ์ ์ํ๋ค.
- "์คํ API ์ด์ฉ ์ ์ฒญ" ๋ฒํผ์ ํด๋ฆญํ๋ค.
- ๋ค์ด๋ฒ ๋ก๊ทธ์ธ์ ์งํํ๋ค.
- ์ ํ๋ฆฌ์ผ์ด์
์ด๋ฆ๊ณผ ์๋น์ค ํ๊ฒฝ์ ์ถ๊ฐํ ๋ค "๋ฑ๋กํ๊ธฐ"๋ฅผ ํด๋ฆญํ๋ค.
- Client ID์ Client Secret์ ํ์ธํ๋ค.
๐ก API ์ด์ฉ ๋ฐฉ๋ฒ
๋ค์ด๋ฒ ์ผํ ๊ฒ์ API์ ๋ํ ์ค๋ช ์ https://developers.naver.com/docs/search/shopping/์ ๋์์๋ค.
JSON ํ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ณ ์ถ๋ค๋ฉด, https://openapi.naver.com/v1/search/shop.json์ GET ๋ฐฉ์์ผ๋ก ์์ฒญ์ ๋ณด๋ด๋ฉด ๋๋ค.
์์ฒญ ๋ณ์ query์ ๊ฒ์์ ์ํ๋ ๋ฌธ์์ด์ ๋ด์์ ์์ฒญ์ ๋ณด๋ด๋ฉด ๋๋ค. ์ถ๋ ฅ ๊ฒฐ๊ณผ items์ ์ํ๋ค์ ๋ํ ์ ๋ณด๊ฐ JSON ํ์์ผ๋ก ์ ๋ฌ๋๋ค.
์์ฒญ์ ๋ณด๋ผ ๋๋ ๋ฐ๊ธ๋ฐ์ Client ID์ Client Secret ์ ๋ณด๋ฅผ ํจ๊ป ๋๊ฒจ์ค์ผ ํ๋ค.
๐ก API ์ด์ฉ ํ๊ธฐ
์ ๋ง ํธ๋ฆฌํ๊ฒ๋ ARC์์ ์์ฒญ Method, URL, Header(Client ID, Client Secret ํฌํจ) ์ ๋ณด๋ฅผ ์ ๋ ฅํ๋ฉด ํด๋น ์์ฒญ์ ์คํ๋ง ์ฝ๋๋ฅผ ํ์ธํ ์ ์๋ค.
RestTemplate rest = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("X-Naver-Client-Id", "๋ฐ๊ธ๋ฐ์ Client ID");
headers.add("X-Naver-Client-Secret", "๋ฐ๊ธ๋ฐ์ Client Secret");
String body = "";
HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
ResponseEntity<String> responseEntity = rest.exchange("https://openapi.naver.com/v1/search/shop.json?query=adidas", HttpMethod.GET, requestEntity, String.class);
HttpStatus httpStatus = responseEntity.getStatusCode();
int status = httpStatus.value();
String response = responseEntity.getBody();
System.out.println("Response status: " + status);
System.out.println(response);
return response;
์ ์ฝ๋ ์์๋ adidas๋ผ๋ ํค์๋๋ก ๊ฒ์์ ์์ฒญํ ๊ฒฝ์ฐ์ด๋ค.
๐ก Naver Shop Search
์ด์ ๊ฒ์์ ์งํํ ํด๋์ค๋ฅผ ์์ฑํ์ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค.
- ํ๋ก์ ํธ์ utils ํจํค์ง๋ฅผ ๋ง๋ ๋ค.
- utils ํจํค์ง ์์ NaverShopSearch ํด๋์ค๋ฅผ ์์ฑํ๋ค.
- public์ผ๋ก search ํจ์๋ฅผ ๋ง๋ค๊ณ ์ ์ฝ๋๋ฅผ ๋ถ์ฌ๋ฃ๋๋ค.
์์ง ๋ถ์กฑํ๊ฒ ์๋ค.๐ค ๋ด๊ฐ ์ํ๋ ๊ฒ์ ํค์๋๋ฅผ ๋๊ฒจ์ฃผ๋ฉด ํด๋น ํค์๋์ ๋ํ ๊ฒ์์ ์งํํ๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ search ํจ์์์ ํค์๋๋ฅผ ๋ฐ๋๋ก ํ๊ณ , responseEntity๋ฅผ ์์ฑํ ๋ ํด๋น ํค์๋๋ฅผ query ๊ฐ์ผ๋ก ๋๊ฒจ์ฃผ๋๋ก ์์ ํด์ผ ํ๋ค.
public class NaverShopSearch {
public String search(String query) {
RestTemplate rest = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("X-Naver-Client-Id", "๋ฐ๊ธ๋ฐ์ Client ID");
headers.add("X-Naver-Client-Secret", "๋ฐ๊ธ๋ฐ์ Client Secret");
String body = "";
HttpEntity<String> requestEntity = new HttpEntity<String>(body, headers);
// ๋๊ฒจ๋ฐ์ query๋ก ๊ฒ์ ์์ฒญ
ResponseEntity<String> responseEntity = rest.exchange("https://openapi.naver.com/v1/search/shop.json?query=" + query, HttpMethod.GET, requestEntity, String.class);
HttpStatus httpStatus = responseEntity.getStatusCode();
int status = httpStatus.value();
String response = responseEntity.getBody();
System.out.println("Response status: " + status);
System.out.println(response);
return response;
}
}
๐ก Item DTO
๋๋๋ ๋ถ์กฑํ๊ฒ ์๋ค.๐ค ๋๋ ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฌธ์์ด์ด ์๋, ItemDto๋ก ๋ฐ๊ณ ์ถ๋ค. ItemDto๋ ๊ฒ์ ๊ฒฐ๊ณผ๋ก ์ค๋ ์ํ๋ค์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ๋ฌผ๊ณ ๋ค๋๋ ๊ฐ์ฒด์ด๋ค. ItemDto ํด๋์ค๋ ๋ค์๊ณผ ๊ฐ๋ค.
@Getter
public class ItemDto {
private String title;
private String link;
private String image;
private int lprice;
public ItemDto(JSONObject itemJson) {
this.title = itemJson.getString("title");
this.link = itemJson.getString("link");
this.image = itemJson.getString("image");
this.lprice = itemJson.getInt("lprice");
}
}
response๋ฅผ ItemDto๋ก ๋ฐ๊พธ๊ธฐ ์ํด์ ๋ค์ ๊ณผ์ ์ด ํ์ํ๋ค.
- ๋ฌธ์์ด ์ ๋ณด๋ฅผ JSONObject๋ก ๋ฐ๊พธ๊ธฐ
- JSONObject์์ items ๋ฐฐ์ด ๊บผ๋ด๊ธฐ
- items ๋ฐฐ์ด์ด ๋ด๊ธด JSONArray์ ์์๋ฅผ ํ๋์ฉ JSONObject๋ก ๋ฐ๊ธฐ
- ๋ฐ์ JSONObject๋ก ItemDto ์์ฑ ํ๊ธฐ
JSONObject์ JSONArray๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด https://mvnrepository.com/์์ JSON In Java ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ gradle์ dependencies์ ์ถ๊ฐํด์ผ ํ๋ค.
๊ทธ ๋ค์, NaverShopSearch์ fromJSONtoItems ํจ์๋ฅผ ์ถ๊ฐํ๋ค.
public List<ItemDto> fromJSONtoItems(String result) {
JSONObject rjson = new JSONObject(result);
JSONArray items = rjson.getJSONArray("items");
List<ItemDto> ret = new ArrayList<>();
for (int i=0; i<items.length(); i++) {
JSONObject itemJson = items.getJSONObject(i);
ItemDto itemDto = new ItemDto(itemJson);
ret.add(itemDto);
}
return ret;
}
๐ก Search Request Controller
๋๋๋๋๋ ๋ถ์กฑํ ์ ์ด ์๋ค.๐ค NaverShopSearch๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ๋งค์ฐ ๋งค์ฐ ์ค์ํ ์์ ์ด ๋จ์๋ค. ํ๋ก์ ํธ ์๊ตฌ ์กฐ๊ฑด์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ฌ์ฉ์๊ฐ ๊ฒ์์ด๋ฅผ ์ ๋ ฅํ๋ฉด ์ปจํธ๋กค๋ฌ๊ฐ ๊ทธ๊ฒ์ ์ ๋ฌ ๋ฐ๋๋ค.
- ์ ๋ฌ๋ฐ์ ๊ฒ์์ด๋ก ๋ค์ด๋ฒ API์ ์์ฒญํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฉ์์๊ฒ ์๋ตํ๋ค.
๋ฐ๋ผ์ Controller๊ฐ NaverShopSearch๋ฅผ ์ฌ์ฉํ ์ ์์ด์ผ ํ๋ค. ์ฌ์ฉ์๊ฐ ์์ฒญ์ ํ๋ฉด NaverShopSearch ํด๋์ค๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด ์ฃผ๋ฉด ์ ๋ง ์ ๋ง ์ข์ ํ ๋ฐ... ์ด๊ฒ ์คํ๋ง์์๋ ๊ฐ๋ฅํ๋ค..! ์คํ๋ง์ด ์๋์ผ๋ก ํ์ํ ํด๋์ค๋ฅผ ํ์ํ ๊ณณ์ ์์ฑํ๋๋ก ํ๋ ค๋ฉด "๋ชฉ๋ก"์ ํด๋น ํด๋์ค๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค. ๊ทธ ๋ชฉ๋ก์ ๋ฑ๋กํ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก ์ปดํฌ๋ํธ ๋ฑ๋ก์ด๋ค.
ํด๋์ค ์์ @Component๋ฅผ ๋ถ์ฌ์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
@Component // @RequiredArgsConstructor ์ ํจ๊ป ์ฌ์ฉํ ๊ฒฝ์ฐ ์คํ๋ง์ด ์๋์ผ๋ก ์์ฑ
public class NaverShopSearch {
๊ฐ์๊ธฐ ๋ ์๋ฌธ,, ๊ทธ๋ผ ์ง๊ธ๊น์ง ์ผ๋ ์๋น์ค๋ ๋ฆฌํฌ์งํ ๋ฆฌ๋ ์ด๋ป๊ฒ ๊ฐ์ ธ๋ค ์ด๊ฑฐ์ง,,? ๋ถ๋ช Controller์๋ง @RequiredArgsConstructor๋ฅผ ์ถ๊ฐํ๊ณ , ์๋น์ค๋ ๋ฆฌํฌ์งํ ๋ฆฌ์๋ ์ปดํฌ๋ํธ ๋ฑ๋ก์ ์ ํด์คฌ๋๋ฐ,,๋ผ๊ณ ์๊ฐํ๋ ๋ด๊ฐ ๋ฐ๋ณด์๋ค~~
https://namocom.tistory.com/421 ๊ธ์ ์ฐธ๊ณ ํ์๋ฉด, @Component์ ๊ตฌ์ฒดํ๋ ํํ๋ก @Repository, @Service, @Controller๊ฐ ์๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด์ ๋จ์ ๊ฒ์ ์ฌ์ฉ์์ ์์ฒญ์ ๋ฐ์ Controller๋ฅผ ๋ง๋๋ ๊ฒ์ด๋ค. ๊ฒ์ ํจ์๋ฅผ ๋ค ๋ง๋ค์๊ธฐ ๋๋ฌธ์ GetMapping์์ ์ฌ์ฉ๋ง ํ๋ฉด๋๋ฏ๋ก ๊ฐ๋จํ๋ค.
@RequiredArgsConstructor // final ๋ก ์ ์ธ๋ ํด๋์ค๋ฅผ ์๋์ผ๋ก ์์ฑ
@RestController // JSON์ผ๋ก ์๋ตํจ์ ์ ์ธ
public class SearchRequestController {
private final NaverShopSearch naverShopSearch;
@GetMapping("/api/search")
public List<ItemDto> getItems(@RequestParam String query) {
String resultString = naverShopSearch.search(query);
return naverShopSearch.fromJSONtoItems(resultString);
}
}
์ด๋ก์จ <๋๋ง์ ์ ๋ ์ต>์ ์ฒซ๋ฒ์งธ ์๊ตฌ ๊ธฐ๋ฅ์ ์์ฑํ๋ค!!!
์ํ๋ช ์ ๋ฐ๋ฅธ ๊ฒ์- ๊ด์ฌ ์ํ ๋ฑ๋ก & ์กฐํ
- ๊ด์ฌ ์ํ์ ๋ํ ์ต์ ๊ฐ ๋ฑ๋กํ๊ธฐ
์ฐธ๊ณ ์๋ฃ: ์คํ๋ฅดํ์ฝ๋ฉํด๋ฝ ์น๊ฐ๋ฐ์ ๋ด, Spring 4์ฃผ์ฐจ ๊ฐ์์๋ฃ