2024. 2. 7. 14:48ใSpring/[2024] Spring Boot
https://hyejin.tistory.com/1290
[Spring] ์นด์นด์ค ๋ก๊ทธ์ธ API ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
์์ฆ์ ์นด์นด์ค, ๋ค์ด๋ฒ๋ฅผ ์ด์ฉํ Login Api ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์ค์ ์ฌ์ฉํ๊ธฐ ์ ์ ์ํ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด kakao login api ๋์ ๋ฐฉ์์ ์์๋ณผ ๊ฒ์ด๋ค. REST API๋ฅผ ์ฌ์ฉํ ์นด์นด์ค ๋ก๊ทธ์ธ์ PC ๋ฐ
hyejin.tistory.com
-> ์นด์นด์ค ๋ก๊ทธ์ธ api ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
์นด์นด์ค ๋ก๊ทธ์ธ api๋ฅผ ์ฌ์ฉํด์ ๋ก๊ทธ์ธ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ณธ ๋ค์,
์ด์ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ api๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ ๊ฒ์ด๋ค.
ํ๋ฒ ํด๋ณด๋ฉด ๋๋ค ํ๋ก์ฐ๋ ๋น์ทํ๊ธฐ ๋๋ฌธ์ ๊ธ๋ฐฉํ ์ ์๋ค.
Naver ๋ก๊ทธ์ธ ๋์ ๋ฐฉ๋ฒ
https://developers.naver.com/main/
NAVER Developers
๋ค์ด๋ฒ ์คํ API๋ค์ ํ์ฉํด ๊ฐ๋ฐ์๋ค์ด ๋ค์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ์ ์๋๋ก API ๊ฐ์ด๋์ SDK๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ ๊ณต์ค์ธ ์คํ API์๋ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ, ๊ฒ์, ๋จ์ถURL, ์บก์ฐจ๋ฅผ ๋น๋กฏ ๊ธฐ๊ณ๋ฒ์ญ, ์
developers.naver.com
1๏ธโฃ ์ ํ๋ฆฌ์ผ์ด์ ๋ฑ๋ก
naver๋ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ค์ด๋ฒ ๊ฐ๋ฐ์ ์ผํฐ์ ๋ค์ด๊ฐ์ ๋ก๊ทธ์ธ ํ ๋ณธ์ธ์ ์๋น์ค ํ๊ฒฝ ํ์ ํ ์ ํ๋ฆฌ์ผ์ด์ ๋ฑ๋กํด์ผ ํ๋ค.
[Application - ์ ํ๋ฆฌ์ผ์ด์ ๋ฑ๋ก]
https://developers.naver.com/docs/common/openapiguide/appregister.md
์ฌ์ ์ค๋น ์ฌํญ - Open API ๊ฐ์ด๋
์ฌ์ ์ค๋น ์ฌํญ ๋ค์ด๋ฒ ์คํAPI๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๋จผ์ ๋ค์ด๋ฒ ๊ฐ๋ฐ์ ์ผํฐ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฑ๋กํ๊ณ ํด๋ผ์ด์ธํธ ์์ด๋์ ํด๋ผ์ด์ธํธ ์ํฌ๋ฆฟ์ ๋ฐ๊ธ๋ฐ์์ผ ํฉ๋๋ค. ํด๋ผ์ด์ธํธ ์์ด๋์ ํด๋ผ
developers.naver.com
-> ์ ํ๋ฆฌ์ผ์ด์ ๋ฑ๋ก ๊ฐ์ด๋
-> API ์ด์ฉํ๊ธฐ ์ํด์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฑ๋กํ๋๋ฐ ์ด๋ฒ์๋ ๋๋ TEST ์ฉ์ด๊ธฐ ๋๋ฌธ์ ๊ทธ๋ฅ ์ ํ๋ฆฌ์ผ์ด์ ์ด๋ฆ์ test๋ก ์ค์ ํ๋ค. ์ฌ์ฉ api์ผ๋ก๋ ๋ก๊ทธ์ธ api๋ฅผ ์ด์ฉํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ์ ์ ํํด์ค๋ค.
๊ทธ๋ฆฌ๊ณ ๋ก๊ทธ์ธ ํ ๋ ์ฌ์ฉ์ ์ ๊ณต ์ ๋ณด๋ฅผ ๋ณ๋ช , ํ๋กํ ์ฌ์ง, ์ด๋ฉ์ผ, ํด๋์ ํ๋ฒํธ ์ ๋๋ง ๊ฐ์ ธ์ค๋๋ก ์ค์ ํ๋ค.
์นด์นด์ค์ ๋์ผํ๊ฒ ํด๋ผ์ด์ธํธ์์ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์์ฒญํ๋ฉด ์ธ์ฆ ๋ฐ ์ ๋ณด ๋์ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๊ณ , ์ด์ ๋ํด ์ฌ์ฉ์๊ฐ ๋์ํ๋ฉด ์ฝ๋ฐฑํด์ค Redirect Uri๋ฅผ ์์ฑํ๋ค.
ํ ์คํธ์ด๊ธฐ ๋๋ฌธ์ ์๋น์ค url ์ http://localhost:8080 ์ด๊ณ ,
๋ค์ด๋ฒ ๋ก๊ทธ์ธ Callback url ์ http://localhost:8080/login/naver/code๋ก ์ง์ ํด์คฌ๋ค.
2๏ธโฃ ์ ํ๋ฆฌ์ผ์ด์ ๋ฑ๋ก ํ์ธ ๋ฐ ํด๋ผ์ด์ธํธ ์์ด๋์ ์ํฌ๋ฆฟ ํ์ธ
-> ๋ด ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฉด ์ด๋ ๊ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฑ๋ก๋ ๊ฒ์ ํ์ธํ ์ ์๊ณ , Client ID์ Client Secret์ด ์๋ค.
์ด ์ ๋ณด๊ฐ ์์ด์ผ ์ธ๊ฐ ์์ฒญ, ์ก์ธ์ค ํ ํฐ, ์ฌ์ฉ์ ์ ๋ณด ์กฐํ ๋ฑ์ ์ํํ ์ ์๋ค.
Client ID์ Client Secret์ ๋ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ๋ถํด์ฃผ๋ ์ค์ํ ์ ๋ณด์ ๋๋ค. ๋ฐ๋์ ์์ ํ๊ฒ ๋ณด๊ดํ์๊ธฐ ๋ฐ๋๋๋ค.
๋ํ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ฐ๋ ๊ณผ์ ์์ ํ์ฉ๋๋ ์ ๋ณด์ ๋๋ค. ์๋ชป๋ Client ID / Client Secret ์ ๋ณด๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด ์ฐ๋์ด ์คํจํ ์ ์์ต๋๋ค.
ํ ๋ฒ ๋ฐ๊ธ์ด ๋ Client ID๋ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค. ํ์ง๋ง Client Secret ์ ๋ณด๋ ๊ฐ๋ฐ์์ผํฐ๋ฅผ ํตํด ์ฌ๋ฐ๊ธ ๋ฐ๋๊ฒ์ด ๊ฐ๋ฅํฉ๋๋ค.
Client Secret์ ์ ์ถ์ด ์์ฌ๋๋ฉด ์ฌ๋ฐ๊ธ์ ํตํด ๋์ฉ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
* ๋ก๊ทธ์ธ ๋ฒํผ ์ฌ์ฉ ๊ฐ์ด๋
https://developers.naver.com/docs/login/bi/bi.md
๋ก๊ทธ์ธ ๋ฒํผ ์ฌ์ฉ ๊ฐ์ด๋ - LOGIN
๋ค์ด๋ฒ ๋ก๊ทธ์ธ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฉํ ์ ์๋ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ๋ฒํผ ๊ธฐ๋ณธ ์ด๋ฏธ์ง๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํฉ์ ๋ง๊ฒ ๋ฒํผ ์ด๋ฏธ์ง์ ๋์์ธ์ ๋ณ๊ฒฝํ ์ ์์ง๋ง ๋ค์ด๋ฒ ๊ณ ์ ์ ์์ด๋ด
developers.naver.com
๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ํ ํ๋ก์ ํธ ๋ง๋ค๊ธฐ
๋ค์ด๋ฒ ๋ก๊ทธ์ธ ๊ณผ์ โ
1. ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ฐ๋ URL ์์ฑํ๊ธฐ
2. ์ ๊ทผ ํ ํฐ ๋ฐ๊ธ ์์ฒญ
3. ์ ๊ทผ ํ ํฐ์ ์ด์ฉํ์ฌ ํ๋กํ API ํธ์ถํ๊ธฐ
1๏ธโฃ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ฐ๋ URL ์์ฑํ๊ธฐ
๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ฐ๋์ ์งํํ๊ธฐ ์ํด์๋ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ๋ฒํผ์ ํด๋ฆญํ์ ๋ ์ด๋ํ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ url ์ ๋จผ์ ์์ฑํด์ผ ํ๋ค. ์ด ๊ณผ์ ์์ ์ฌ์ฉ์๋ ๋ค์ด๋ฒ์ ๋ก๊ทธ์ธ ์ธ์ฆ์ ์ํํ๊ณ , ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ฐ๋ ๋์ ๊ณผ์ ์ ์ํํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ ์ฐ๋์ ๋์ํ๋ฉด ๋์ ์ ๋ณด๋ฅผ ํฌํจํ์ฌ Callback url ๋ก ์ ์กํ๋ค.
์์ฒญ๋ฌธ ์ํ
https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=CLIENT_ID&state=STATE_STRING&redirect_uri=CALLBACK_URL
application-private.yml
naver:
client_id:
redirect_uri :
client_secret:
kakao ์ ๋ณด์ ๋์ผํ๊ฒ naver๋ client_id , client_secret, redirect_uri ์ ๋ณด๋ค์ ์ ๋ ฅํด์ ๋ฃ์ด์ค๋ค.
NaverApi
@Slf4j
@Getter
@Component(value = "naverApi")
public class NaverApi
{
@Value("${naver.client_id}")
private String naverClientId;
@Value("${naver.redirect_uri}")
private String naverRedirectUri;
@Value("${naver.client_secret}")
private String naverClientSecret;
}
-> ๊ทธ๋ฆฌ๊ณ yml ์ ์ ์ฅํ ์ ๋ณด๋ค์ ๋ฐ๋ vo ๊ฐ์ฒด๋ฅผ ํ๋ ๋ง๋ค์ด์ค๋ค.
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kakao & Naver Login API TEST</title>
</head>
<body>
<div class="text-center">
<a href="https://kauth.kakao.com/oauth/authorize"
th:href="@{https://kauth.kakao.com/oauth/authorize(client_id=${kakaoApiKey}, redirect_uri=${kakaoRedirectUri}, response_type='code')}">
<img src="/images/kakao_login_medium_narrow.png">
</a>
<p> </p>
<a href="https://nid.naver.com/oauth2.0/authorize"
th:href="@{https://nid.naver.com/oauth2.0/authorize(client_id=${naverClientId}, redirect_uri=${naverRedirectUri}, response_type='code', state=${state})}">
<img src="/images/btnG.png" style="width: 100px; height: 35px" >
</a>
</div>
</body>
</html>
-> kakao login button ์๋ login ํ์ด์ง์ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ์ ๋ณด๋ ๋ฃ์ด์ค๋ค.
TestController
@GetMapping("/login")
public String loginForm(Model model)
{
model.addAttribute("kakaoApiKey", kaKaoApi.getKakaoApiKey());
model.addAttribute("kakaoRedirectUri", kaKaoApi.getKakaoRedirectUri());
model.addAttribute("naverClientId", naverApi.getNaverClientId());
model.addAttribute("naverRedirectUri",naverApi.getNaverRedirectUri());
model.addAttribute("state", "STATE_STRING");
return "login";
}
-> login ์ฝ๋์ naverClientId, naverRedirectUri ์ ๋ณด๋ฅผ ๋ด์์ login ํ์ด์ง์ ์ ๋ฌํ๋ค.
์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ ํ๊ณ ์ ๋ณด ์์ฒญ์ ๋์ํ๋ฉด ๊ทธ ๋ค์์ Callback url๋ก ์ก์ธ์ค ํ ํฐ ๋ฐ๊ธ์ ์ํ code๋ฅผ ์ ๋ฌํ๋ค.
2๏ธโฃ ์ ๊ทผ ํ ํฐ ๋ฐ๊ธ ์์ฒญ
์ด์ Callback ์ผ๋ก ์ ๋ฌ๋ฐ์ ์ ๋ณด๋ฅผ ์ด์ฉํ์ฌ ์ ๊ทผ ํ ํฐ์ ๋ฐ๊ธ๋ฐ์ ์ ์๋ค.
์ด๋ ์ ๊ทผ ํ ํฐ์ ์ฌ์ฉ์๊ฐ ์ธ์ฆ์ ์๋ฃํ๋ค๋ ๊ฒ์ ๋ณด์ฅํ ์ ์๋ ์ธ์ฆ ์ ๋ณด์ด๋ค. ์ด ์ ๊ทผ ํ ํฐ์ ์ด์ฉํด ํ๋กํ api๋ฅผ ํธ์ถํ๊ฑฐ๋ ์คํ api๋ฅผ ํธ์ถํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
'code' ๊ฐ์ ์ด์ฉํ APIํธ์ถ์ ์ต์ด 1๋ฒ๋ง ์ํํ ์ ์์ผ๋ฉฐ ์ ๊ทผ ํ ํฐ ๋ฐ๊ธ์ด ์๋ฃ๋๋ฉด ์ฌ์ฉ๋ 'code'๋ ๋ ์ด์ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
์์ฒญ๋ฌธ ์ํ
https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id=jyvqXeaVOVmV&client_secret=527300A0_COq1_XV33cf&code=EIc5bFrl4RibFls1&state=9kgsGTfH4j7IyAkg
NaverApi
public String getAccessToken(String code, String state)
{
String reqUrl = "https://nid.naver.com/oauth2.0/token";
RestTemplate restTemplate = new RestTemplate();
// HttpHeader Object
HttpHeaders headers = new HttpHeaders();
// HttpBody Object
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "authorization_code");
params.add("client_id", naverClientId);
params.add("client_secret", naverClientSecret);
params.add("code", code);
params.add("state", state);
// http body params ์ http headers ๋ฅผ ๊ฐ์ง ์ํฐํฐ
HttpEntity<MultiValueMap<String, String>> naverTokenRequest = new HttpEntity<>(params, headers);
// reqUrl๋ก Http ์์ฒญ, POST ๋ฐฉ์
ResponseEntity<String> response = restTemplate.exchange(reqUrl,
HttpMethod.POST,
naverTokenRequest,
String.class);
String responseBody = response.getBody();
JsonObject asJsonObject = JsonParser.parseString(responseBody).getAsJsonObject();
return asJsonObject.get("access_token").getAsString();
}
-> https://nid.naver.com/oauth2.0/token url๋ก ์ด์ code ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์ก์ธ์ค ์ ๋ณด๋ฅผ ์์ฒญํ๋ค.
grant_type์ ์ก์ธ์ค ํ ํฐ ์ ๋ณด ๋ฐ๊ธ์ด๊ธฐ ๋๋ฌธ์ "authorization_code"๋ก ๊ณ ์ ํ๋ค.
client_id, client_secret ์ yml ์์ ๊ฐ์ ธ์จ ๊ฐ์ ๋ฃ์ด์ฃผ๊ณ code๋ ์ฝ๋ฐฑํ ๋ ๋ฐ์ ์ ๋ณด๋ฅผ ์ ๋ ฅํด์ค๋ค.
-> ์์ฒญ์ ๋ํ ์๋ต์ ๋ณด๋ก access_token์ ๋ฐ์์ ์ด์ ์ด๊ฑธ ํตํด ์ฌ์ฉ์ ํ๋กํ api๋ฅผ ํธ์ถํ ์ ์๋ค.
3๏ธโฃ ์ ๊ทผ ํ ํฐ์ ์ด์ฉํ์ฌ ํ๋กํ API ํธ์ถํ๊ธฐ
-> ์ ๊ทผ ํ ํฐ์ ์ด์ฉํ๋ฉด ํ๋กํ ์ ๋ณด ์กฐํ API๋ฅผ ํธ์ถํ๊ฑฐ๋ ์คํ API๋ฅผ ํธ์ถํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
๊ทผ๋ฐ ์ฌ์ฉ์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ํ๋ํ๊ธฐ ์ํด์๋ ์ฐ์ ํ๋กํ ์ ๋ณด ์กฐํ API๋ฅผ ๋จผ์ ํธ์ถํด์ผ ํ๋ค.
์ฌ๊ธฐ์ ๊ฐ์ ธ์จ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์ด์ ์ฐ๋ฆฌ ์๋น์ค์ ๋ฑ๋ก๋ ํ์์ธ์ง ์๋์ง ํ๋จ ํ ์์ผ๋ฉด ํ์๊ฐ์ ์งํํ๊ณ , ์์ผ๋ฉด ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๋ฅผ ํ๋ฉด ๋๋ค.
NaverApi
public NaverProfile getUserInfo(String accessToken)
{
String reqUrl = "https://openapi.naver.com/v1/nid/me";
RestTemplate restTemplate = new RestTemplate();
// HttpHeader ์ค๋ธ์ ํธ
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
HttpEntity<MultiValueMap<String, String>> naverProfileRequest = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(reqUrl,
HttpMethod.POST,
naverProfileRequest,
String.class);
System.out.println("response = " + response);
NaverProfile naverProfile = new NaverProfile(response.getBody());
return naverProfile;
}
-> https://openapi.naver.com/v1/nid/me ์ด url ์ ํตํด ์์ฒญ ํค๋์ Authorization์ ์ ๊ทผ ํ ํฐ์ ๋ฃ์ด ์ ๋ฌํ๋ฉด ๋๋ค.
์์ฒญ๋ฌธ ์์
curl -XGET "https://openapi.naver.com/v1/nid/me" \
-H "Authorization: Bearer AAAAPIuf0L+qfDkMABQ3IJ8heq2mlw71DojBj3oc2Z6OxMQESVSrtR0dbvsiQbPbP1/cxva23n7mQShtfK4pchdk/rc="
TestController
@ResponseBody
@GetMapping("/login/naver/code")
public Map<String, Object> naverLogin(@RequestParam(name = "code") String code, @RequestParam(name = "state") String state)
{
Map<String, Object> map = new HashMap<>();
// 1. ์ธ๊ฐ ์ฝ๋ ๋ฐ๊ธฐ -> @RequestParam String code
// 2. ์ ๊ทผ ํ ํฐ ๋ฐ๊ธ ์์ฒญ
String accessToken = naverApi.getAccessToken(code, state);
System.out.println("accessToken = " + accessToken);
// 3. ์ฌ์ฉ์ ์ ๋ณด ๋ฐ๊ธฐ
NaverProfile userInfo = naverApi.getUserInfo(accessToken);
map.put("id", userInfo.getId());
map.put("nickName", userInfo.getNickname());
map.put("email", userInfo.getEmail());
map.put("mobile", userInfo.getMobile());
return map;
}
-> getUserInfo ์ ํตํด ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ์์ NaverProfile์ ์์ฑ์๋ก ์ ๋ฌํด ๊ฐ์ฒด ์์ฑํ๋ค.
NaverProfile
@Getter
public class NaverProfile
{
private String id;
private String nickname;
private String email;
private String mobile;
public NaverProfile(String jsonResponseBody)
{
JsonParser jsonParser = new JsonParser();
JsonElement element = jsonParser.parse(jsonResponseBody);
this.id = element.getAsJsonObject().get("response").getAsJsonObject().get("id").getAsString();
this.nickname = element.getAsJsonObject().get("response").getAsJsonObject().get("nickname").getAsString();
this.email = element.getAsJsonObject().get("response").getAsJsonObject().get("email").getAsString();
this.mobile = element.getAsJsonObject().get("response").getAsJsonObject().get("mobile").getAsString();
}
}
-> ์คํํ๋ฉด์ด๊ณ ์ฌ๊ธฐ์ ๋ก๊ทธ์ธ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ๋ก๊ทธ์ธ ์ํํ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ json์ผ๋ก ์ถ๋ ฅํ๋ ํ๋ฉด์ผ๋ก ์ฐ์ ๋ง๋ฌด๋ฆฌ ํ๋ค.
-> ์ด ํ๋ก์ ํธ?๋ ๋จ์ํ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ api๋ฅผ ์ฌ์ฉํด๋ณด๋ ์ํ ํ๋ก์ ํธ์ด๊ณ ์ด์ ์ฌ๊ธฐ์ ๋ ๋์๊ฐ ํ์ ๊ฐ์ ๋ฐ ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๋ ์ง์ ๋ง๋ค์ด์ ์ฌ์ฉํด์ฃผ๋ฉด ๋๋ค.