RestController
์์ Success/Fail์ ๋ํ ์ฌ๋ถ๋ฅผ ๋ฐํํด์ฃผ๊ธฐ ์ํ ํด๋์ค
public class RsData<T> {
private String resultCode;
private String msg;
private T data;
public static <T> RsData<T> of(String resultCode, String msg) {
return new RsData<>(resultCode, msg, null);
}
}
- ์๋ ์์์ ๊ฐ์ด ์ฑ๊ณต๊ณผ ์คํจ์ ๋ํด ๋ ์์ธํ ์ ๋ณด๋ฅผ ์ ๊ณตํ ์ ์๋ค.
- ์ฑ๊ณต : ์ ์ ๋ก๊ทธ์ธ || ํด๋จผํ์(์ผ๋ถ ๊ธฐ๋ฅ ์ฌ์ฉ ๊ฐ๋ฅ)
- ์คํจ : ์์ด๋ ๋ถ์ผ์น || ๋น๋ฐ๋ฒํธ ๋ถ์ผ์น || ์ผ์ ์ค์ง || ์๊ตฌ ์ ์ง
GET /articles
- ์ ์์ฒญ์ด ๋ค์ด์์ ๋
articles
์ ์ถ๋ ฅ์ ํด์คdata
๋ฅผ ๋ด์์ ๋ณด๋ด์ค ์ ์๋ค.
Json
,HTTP
๊ณผ ๊ด๋ จ๋ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ๋ ํด๋์ค
// Util.spring
public static <T> ResponseEntity<RsData> responseEntityOf(RsData<T> rsData) {
return responseEntityOf(rsData, null);
}
- ๋ฐ์ดํฐ๋ฅผ ํ์ ์ ์ผ๋ก ์ง์ ํด์ ์ฌ์ฉํด์ฃผ๊ธฐ ์ํด ์ฌ์ฉ
// Util.json
public static <K, V> Map<K, V> mapOf(Object... args) {
...
}
{
"resultCode": "S-1",
"msg": "๋ก๊ทธ์ธ ์ฑ๊ณต, Access Token์ ๋ฐ๊ธํฉ๋๋ค.",
"data": {
"Authentication": "JWT_Access_Token",
"username": "abc123"
},
"success": true,
"fail": false
}
- ์์ ๊ฐ์ด
data
์๋ ์ฌ๋ฌ ์ ๋ณด๊ฐ ๋ค์ด๊ฐ ์ ์์ด์ผํ๋ค. - ๋๋ฌธ์ ๊ฐ๋ณ์ธ์๋ฅผ ์ฌ์ฉํด ๊ธธ์ด์ ์ ํ์ ๋์ง ์๊ณ , ์ฌ๋ฌ ์ ๋ณด๋ฅผ ์ถ๊ฐํด์ค๋ค.
์์์ ์ด๋ฆ(์์์ ํํ)์ผ๋ก ๊ตฌ๋ถํ์ฌ ํด๋น ์์์ ์ํ(์ ๋ณด)๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ชจ๋ ๊ฒ์ ์๋ฏธ
- ๊ฒ์๋ฌผ ๋ชฉ๋ก :
GET
http://localhost:8080/article/list - ๊ฒ์๋ฌผ ๋ฑ๋ก(ํผ) :
GET
http://localhost:8080/article/write - ๊ฒ์๋ฌผ ๋ฑ๋ก :
POST
http://localhost:8080/article/write - ๊ฒ์๋ฌผ ๋จ๊ฑด ์กฐํ :
GET
http://localhost:8080/article/1 - ๊ฒ์๋ฌผ ์์ (ํผ) :
GET
http://localhost:8080/article/1/modify - ๊ฒ์๋ฌผ ์์ :
POST
http://localhost:8080/article/1/modify - ๊ฒ์๋ฌผ ์ญ์ :
GET
http://localhost:8080/article/1/delete
- ๊ฒ์๋ฌผ ๋ชฉ๋ก :
GET
http://localhost:8080/articles - ๊ฒ์๋ฌผ ๋ฑ๋ก :
POST
http://localhost:8080/articles - ๊ฒ์๋ฌผ ๋จ๊ฑด ์กฐํ :
GET
http://localhost:8080/article/1 - ๊ฒ์๋ฌผ ์์ :
PATCH
http://localhost:8080/article/1 - ๊ฒ์๋ฌผ ์ญ์ :
DELETE
http://localhost:8080/article/1
์ฃผ๋ก
REST API
๋ฅผ ๊ตฌ์ถ ํ์ ๋, ํ๋ก ํธ ๊ฐ๋ฐ์์๊ฒ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด ์ฌ์ฉํ๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋์๊ฒ ๋ณด์ฌ์ง๊ธฐ ๋๋ฌธ์ ์์ด๋, ๋น๋ฐ๋ฒํธ๋ฅผ ์ง์ ํด์ฃผ๋ ๊ฒ์ด ์ข๋ค.
implementation 'org.springdoc:springdoc-openapi-ui:1.6.11'
- Security
.authorizeRequests(
authorizeRequests -> authorizeRequests
.antMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
)
Swagger
์์ ํ์ธ์ ํด์ผํ๊ธฐ ๋๋ฌธ์ ๊ด๋ จ๋ URL์permitAll
์ ํด์ค๋ค.
๋จ, ์ค์ ์๋น์ค๋ฅผ ์ด์ํ ์์๋ ๋ณด์์ ๊ฑธ์ด์ฃผ๋ ๊ฒ์ด ์ข๋ค.
- Spring Doc
@Bean
public OpenAPI springShopOpenAPI() {
return new OpenAPI()
.info(new Info().title("SpringShop API")
.description("Spring shop sample application")
.version("v0.0.1")
.license(new License().name("Apache 2.0").url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("SpringShop Wiki Documentation")
.url("https://springshop.wiki.github.org/docs"));
}
Swagger
์ ์์ฑํด๋์ ์ค๋ช ๊ณผ ์ ๋ณด๋ฅผ ์์ฑํ๋ค.
Json
ํฌ๋งท์ ์ด์ฉํ์ฌ ์ฌ์ฉ์์ ๋ํ ์์ฑ์ ์ ์ฅํ๋ Claim ๊ธฐ๋ฐ์Web Token
- ํ์ ์ ๋ณด๊ฐ ์ผ์นํ๋์ง ํ์ธ
- ์ธ์ ๋ณ์ ์์ฑ -> ์ํ๋ฆฌํฐ ๋ฐฉ์
@AuthenticationPrincipal MemberContext memberContext
๋ ์ธ์ ์์ ๋ฐ์ดํฐ๋ฅผ ๊บผ๋ด์ด
POST /member/login
- ํ์ ์ ๋ณด๊ฐ ์ผ์นํ๋์ง ํ์ธ
JWT
ํ์์accessToken
์ ๋ฐ๊ธ
header
์Authorization : Bearer accessToken
๋ก ์ ์ฅ๋ ์ดํ ์ธ์ ์ ๋ณด๋ฅผ ์ถ๊ฐํด์ค์ผ์งSecurity
์์ ์ ๊ณตํ๋ ์ด๋ ธํ ์ด์ ์ฌ์ฉ ๊ฐ๋ฅ
๋๋ค์ ๋๋ ์ด๋ฉ์ผ ๊ฐ์ ์ ๋ณด๊ฐ ์ ์ฅ๋์ด ์๊ณ , ํ ํฐ ๋ฐ๊ธ ์ดํ์ ์ฌ์ฉ์๊ฐ ์ด๋ฉ์ผ, ๋๋ค์์ ๋ณ๊ฒฝํ๋ค๋ฉด ์ ๋ณด ๋ถ์ผ์น๊ฐ ๋ฐ์
๋ง์ฝ ํ ํฐ์ ๋ง๋ฃ ๊ธฐ๊ฐ์ 90์ผ๋ก ์ง์ ํ ๋ค ํ์ทจ๋ฅผ ๋นํ๋ค๋ฉด, JWT ํ ํฐ์ ๊ตฌ์กฐ์ ํ๊ธฐ๊ฐ ๋ถ๊ฐ๋ฅํ๋ค.
// MemberService.java
public String generateAccessToken(Member member) {
return jwtProvider.generateAccessToken(member.getAccessTokenClaims(), 60 * 5);
}
5๋ถ๋ง๋ค 1๋ฒ์ฉ ๋ก๊ทธ์ธ์ ํด์ค์ผํ๋ ๋ฒ๊ฑฐ๋ก์์ด ์์ง๋ง,
Refresh Token
์ ์ฌ์ฉํด์ ํด๊ฒฐํ๋ค.
ํด๋ผ์ด์ธํธ์์๋Access Token
๊ณผRefresh Token
์ ๊ฐ์ง๊ณ ์์ด์ผํ๋ค.
Refresh Token
- JWT ํ์์ผ ํ์๋ ์์ผ๋ฉฐ, DB์์ ๊ด๋ฆฌ๋๋ ์์ ํค(๋น๋ฐ๋ฒํธ)๋ผ๊ณ ์๊ฐํ์.
- ํด๋น Refresh Token์ผ๋ก ์ฌ๋ก๊ทธ์ธ ์ฆ, Access Token์ ๋ฐ๊ธ ๋ฐ๋๊ฒ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋ธ๋ ๋ฆฌ์คํธ โก๏ธ ๋ฌธ์ ๊ฐ ๋๋ ์ ๋ค์ ๊ธฐ์ต
ํ์ดํธ ๋ฆฌ์คํธ โก๏ธ ๋ด๊ฐ ์ธ์ ํ ์ ๋ค๋ง ๊ธฐ์ต
- ์ต์ด์ ๋ฐ๊ธ๋ ์ก์ธ์ค ํ ํฐ์ ๊ธฐ๋ก
- 2๋ฒ์งธ ์ดํ๋ก ์์ฒญ๋ ๋ก๊ทธ์ธ์๋ ๊ธฐ์กด ์์ธ์ค ํ ํฐ์ ๋ฐํ
- ์๋ฒ์์ ๋งค ์์ฒญ๋ง๋ค, ์์ธ์ค ํ ํฐ์ด ์ ํจํ์ง ์ฒดํฌํ๋ ๊ฒ์ ๋์ด์, DB์ ์ ์ฅ๋์ด ์๋ ์ธ์ฆ๋ ์์ธ์ค ํ ํฐ์ธ์ง๋ ํ์ธ
Spring Boot
๋ ์ธ๋ถ ์บ์๋ฅผ ์ง์ ํ์ง ์์ผ๋ฉดMap
๊ณผ ๊ฐ์ ๋ด๋ถ ๋ฉ๋ชจ๋ฆฌ ์บ์๋ฅผ ์ฌ์ฉํ๋ค.Application
์์@EnableCaching
์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ค.
SpringBoot
์์ ์บ์๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์๋์ ๊ฐ์ด ๊ฐ๋จํ ์์
ํ๋๋ง ์งํํ๋ฉด ๋๋ค.
// Application.java
@...
@EnableCaching
public class SecurtyExamApplication {
...
}
// MemberService
@Cacheable("key1")
public int getCachedInt() {
System.out.println("ํธ์ถ๋จ");
return 5;
}
// CacheTests
@Test
@DisplayName("์บ์ ์ฌ์ฉ")
void t1() {
int rs = memberService.getCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
rs = memberService.getCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
}
์์ ๊ฐ์ด
@Cachable("keyName")
์ ์ฌ์ฉํ๋ฉด ์ฒ์ ํธ์ถ์ด ๋ ๋keyName
์return
๊ฐ์ ์ ์ฅํ๊ณ ,
๋ค์ ํธ์ถ์ด ๋ ๋๋ถํฐ๋ ํจ์๋ฅผ ๋ค์ ์คํํ๋ ๊ฒ์ด ์๋ ์ ์ฅ๋keyName
์value
๋ฅผ ๋ถ๋ฌ์ค๊ฒ ๋๋ค.
@CacheEvict("key1")
public void deleteCachedInt() {
System.out.println("์ญ์ ๋จ");
}
@Test
@DisplayName("์บ์ ์ฌ์ฉ, ์ญ์ , ์์ฑ")
void t2() {
// ์บ์ ์์ฑ(rs = 5)
int rs = memberService.getCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
// ์บ์ ์ฌ์ฉ(rs = 5)
rs = memberService.getCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
// ์บ์ ์ญ์
memberService.deleteCachedInt();
// ์บ์ ์์ฑ(rs = 5)
rs = memberService.getCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
}
@CachePut("key1")
public int updateCachedInt() {
System.out.println("์์ ๋จ");
return 10;
}
@Test
@DisplayName("์บ์ ์ฌ์ฉ, ์ญ์ , ์์ฑ")
void t2() {
// ์บ์ ์์ฑ(rs = 5)
int rs = memberService.getCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
// ์บ์ ์ฌ์ฉ(rs = 5)
rs = memberService.getCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
// ์บ์ ์์ (rs = 10)
rs = memberService.updateCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
// ์บ์ ์ฌ์ฉ(rs = 10)
rs = memberService.getCachedInt();
assertThat(rs).isGreaterThan(0);
System.out.println("rs = " + rs);
}
Redis๋ key : value๋ก ์ด๋ฃจ์ด์ง NoSQL์ด๋ค.
SpringBoot ๋ด๋ถ ์บ์ : ์๋ฐ ์๋ฃ๊ตฌ์กฐ 100% ํ์ฉ ๊ฐ๋ฅ
SpringBoot Redis ์บ์ : List, Map์ ์ ์ธํ ์ฌ์ฉ์ ์ ์ ๊ฐ์ฒด๋ ์ฌ์ ์ ํด์ฃผ์ด์ผ ํจ
brew install redis
brew services start redis
redis-cli
# build.gradle
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
# application.yml
spring:
cache:
type: redis
redis:
host: 127.0.0.1
port: 6379