ceos back-end 21st naver knowledge-in clone coding project
DB์ JPA๋ฅผ ์งฌ๋ฝ์ํค๋ฉฐ ์๊ฐ ์ ๋ฆฌํ ๊ทธ๋ฆผ
๊ทธ๋ฆผ์์ ์ซ์ด์/์ข์์ ์ฌ๋ถ๋
boolean์ผ๋ก ๊ทธ๋ ธ๋๋ฐ
๊ตฌํํ๋ฉด์ Enum์ผ๋ก ๋ณ๊ฒฝํ๋ค.
๋นจ๊ฐ์ ๊ธ์จ๋ก ์ ์ ๋ถ๋ถ์ cascade ๊ด๋ จ์ธ๋ฐ
๋ฐ์์ ์ดํด๋ณด์.
๊ณผ์ ์ ์์ฑ๋ ๊ฒ ์ธ์ ๋ํ
์ผ์
- ์์ฑ, ์ ๋ฐ์ดํธ ์๊ฐ ๊ธฐ๋ก (BaseTimeEntity)
- ์กฐํ์
- ๋๋ค์ ๊ณต๊ฐ / ๋น๊ณต๊ฐ
- ์ฑํ ์ฌ๋ถ
- ๋๋๊ธ ์๊ธฐ ์ฐธ์กฐ
- ์ฌ์ง ์ ๋ก๋๋ช , storage๋ช ๋ถ๋ฆฌ
- cascade
๊ฐ์ธ์ ์ผ๋ก ์ข ๋ค๋ฒ๋๊ณ ์ ๊น๋จน์ด์..
๊ทธ๋ฆผ์ผ๋ก ๊ทธ๋ฆฌ๋๊ฒ ์ต๊ณ
- ์ ๋ชฉ
- ๋ด์ฉ
- ์์ฑ์๋ช ๊ณต๊ฐ/๋น๊ณต๊ฐ
- ์์ฑ์ผ
- ํด์ํ๊ทธ
- ์กฐํ์
- ๋๊ธ
- ๋๋๊ธ
- ์ซ์ด์/์ข์์
- ์์ฑ์ผ
์ด๋ฒ ๊ณผ์ ์์ ์ฃผ์ ๊ณ ๋ฏผ ํฌ์ธํธ!
ํ๋์ฉ ๋ณด๋ฉด
์ ์ ํํด - soft delete๋ฅผ ์ํด ๋ชจ๋ remove๋ฅผ ๊ฑธ์ง ์์๋ค.
์กฐ๊ธ ๊ธฐํ์ ์ธ ์ธก๋ฉด์ด๊ธฐ๋ ํ์ง๋ง,
๋ง์ ์๋น์ค์์ ๊ฐ์
์ฝ๊ด์ ์ ์ด๋๊ณ
ํํดํด๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด๊ดํ๋ค.
์ ์ ๊ฐ ์ฐ๋ฆฌ ์๋น์ค์ ์์ฑํด์ค ๊ธ๋ ์์ฐ!์ด๋ผ๊ณ ์๊ฐํ๋ค.
๊ตณ์ด ์ง์ฐ๊ณ ํํดํ๋๊ฒ ์๋๋ผ๋ฉด ์ฐ๋ฆฌ๊ฐ ์ญ์ ํด์ค ์ด์ ๊ฐ ์๋ค.
๊ธ ์ญ์ - ๊ธ ์ญ์ ์ ์ฌ์ง์ ๊ฐ์ด ์ญ์
๊ธ์ด ์์ด์ง ์ฌ์ง์ ์๋ฏธ๊ฐ ์๋ ๊ฒ ๊ฐ์์ cascade remove๋ฅผ ๊ฑธ์๋ค.
๋ต๋ณ์ ๋ค๋ ์
์ฅ์์ ๊ธ์ด ์ญ์ ๋์๋ค๊ณ
๋ด๊ฐ ๋ฌ์๋ ๋ต๋ณ์ ๋ชป๋ณด๋ฉด ์กฐ๊ธ ์ด์ํ ๊ฒ ๊ฐ์์ ๊ฑธ์ง ์์๋ค.
ํด์ํ๊ทธ๋ remove๋ฅผ ๊ฑธ์ง ์์๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ํด์ํ๊ทธ๋ก ์ต๊ทผ ์ธ๊ธฐ ์๋ ์ฃผ์ ๊ฐ ๋ฌด์์ธ์ง
์ฝ๊ฒ ํ์
ํ ์ ์๋ ์งํ์ด๊ณ ์ข์ ์์ฐ์ด๊ธฐ ๋๋ฌธ์
์ง์ฐ์ง ์๋๊ฒ ์ข๋ค๊ณ ์๊ฐํ๋ค.
๋๊ธ ์ญ์ - ํด๋น๋๋ ๋๋๊ธ์ ๊ฐ์ด ์ญ์
๋๊ธ์ ๋ฌ๋ฆฐ ์ซ์ด์, ์ข์์ ๊ฐ์ด ์ญ์
์ด๊ฒ ์ ์ผ ๊ณ ๋ฏผํ ๊ฑฐ ์์ด ์ง๊ด์ ์ด๋ค.
์ ๋๋์ง ํ ์คํธ๋ฅผ ํด๋ณด๋ฉด
@Test
@DisplayName("Cascade ์๋ ํ์ธ: Post ์ญ์ ์ PostImage๋ ์ญ์ ")
@Transactional
void deletePostAndCascadeDeleteImages() {
// given
postRepository.delete(userPost);
// when
Optional<Post> findPost = postRepository.findById(userPost.getId());
// then
assertThat(findPost).isEmpty();
userPost.getPostImages().forEach(image -> {
Optional<PostImage> findPostImage = postImageRepository.findById(image.getId());
assertThat(findPostImage).isEmpty();
});
}๊ธ์ ์์ฑํ๋ฉด ๋๋ถ๋ถ ํด์ํ๊ทธ๋ ์ฌ์ง์ ๊ฐ์ด ์
๋ก๋ํ๋ค.
์ด๊ฑด ๋ฐ๋ก ์ฝ๋๋ฅผ ๋ณด์
@Test
@DisplayName("Cascade ์๋ ํ์ธ: ์ฐ๊ด๊ด๊ณ๊ฐ ์๋ Post save")
@Transactional
void savePostTestWithRelation() {
// given
List<PostImage> imageList = make2PostImages();
Post newPost = makeTestPostWithImages(imageList);
// when
Post savedPost = postRepository.save(newPost);
// then
assertThat(newPost).isEqualTo(savedPost);
assertThat(savedPost.getPostImages().size()).isEqualTo(2);
}Post๋ง save ํ๋๋ฐ?
insert ์ฟผ๋ฆฌ๊ฐ ์ ๋๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
cascade ์๊ฑธ์ด์ฃผ๋ฉด
post image ํ๋ ํ๋ save save save ํด์ผํ๋ค.
Comment๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฝ์ด๋ก ๊ฑธ๋ ค ์๋ค๊ณ ํด์
Reply๋ก ์ด๋ฆ์ ๋ณ๊ฒฝํ๋ค
์ปค๋ฒ๋ฆฌ์ง
์ง์ ๊ฒฝํ์ ํ ๊ฒ์ ์๋์ง๋ง
์ด์ผ๊ธฐ๋ฅผ ์กฐ๊ธ ๋ค์ด๋ณด๋ฉด ์ค๋ฌด๋ ์ฐ๋ฆฌ๊ฐ ๊ณต๋ถํ๋ ๊ฒ๊ณผ ์กฐ๊ธ ๋ค๋ฅด๋ค๊ณ ํ๋ค.
ํนํ JPA ์ฐ๊ด๊ด๊ณ ๋งคํ์ด๋ DB foreign key, cascade ์ค๊ณ ๋ถ๋ถ
fk๊ฐ ๊ฑธ๋ ค ์์ผ๋ฉด ํ ์ด๋ธ ์ญ์ , ์ด๋์ด ํ๋ค๋ค. (์๋ง ๊ฒฝํ์ด ์์ ๊ฒ)
๋ํ ์ด์ ํ๊ฒฝ์์ ๋ฐ์ดํฐ๊ฐ ์๋ชป ์ญ์ ๋๊ฑฐ๋
์ ํฉ์ฑ์ด ๊นจ์ง๋ ์ด์ ๋ก fk๋ฅผ ์ ์ก์ง ์๋๋ค๊ณ ํ๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก
JPA๋ฅผ ์ฌ์ฉํ ๋ ์ฐ๊ด๊ด๊ณ๋ฅผ ์ ์ก์ง ์์ ๊ฒ ๊ฐ๋ค.
์ด๋ฒ ๊ณผ์ ์์ JPA๋ฅผ ๊ณต๋ถํด๋ณด๊ธฐ ์ํด
์ฐ๊ด๊ด๊ณ๋ฅผ ๋ชจ๋ ์ก์์ง๋ง, ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค.
๋ฌด์๋ณด๋ค ์ฐ๊ด๊ด๊ณ๋ก ์คํธ๋ ์ค ์๋ฐ๊ณ ๊ฐ๋ฐํ ์ ์๋ค.
๋์ ๊ฒฝ์ฐ ๋์จํ ๊ฒฐํฉ (DB) + ๋จ๋ฐฉํฅ ๋งคํ (JPA)
๋ฐฉ๋ฒ์ ์ ์ฉํ๊ณ ์๋ค.
์ฐ๊ด๊ด๊ณ๊ฐ ์๋ ๊ฒ์ผ๋ก ๋ณด์ด์ง๋ง
pk๊ฐ ์๋ ์๋ณ์๋ฅผ ๋ง๋ค์ด์
ํด๋น ์ปฌ๋ผ์ ์ฐธ์กฐํ๊ณ ์๋ค.
์ด๋ ๊ฒ ๊ตฌํํด๋ Data Grip์ ์ฌ์ฉํ๋ค๋ฉด
virtual foreign key๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์
๋ค์ด์ด ๊ทธ๋จ์ผ๋ก ์ฐธ์กฐ ๊ด๊ณ๋ฅผ ํ์ธํ ์ ์๋ค.
์ธํ
๋ฆฌ์ ์ด์์ ์ด๋ฐ ๊ธฐ๋ฅ์ ๋ง๋ค์ด ๋์๊ฒ์ด
๋ง์ด๋ค ์ด๋ ๊ฒ ํ๋ค๋ ์ฆ๊ฑฐ๊ฐ ์๋๊น?
https://mangkyu.tistory.com/251
Record type
- final ์ ํ์ ์ถ๊ฐ
- ์์ฑ์
- equals
- getter
- hashcode, toString
๊ฒฐ๋ก ๋ถํฐ
๋์ ๊ฒฝ์ฐ default batch size๋ฅผ ๊ฑธ๊ณ ์ผ๋จ ๋์ด๊ฐ๋ค.
์คํธ๋ ์ค ๋ฐ์ผ๋ฉด์ ๋ชจ๋ ๊ฑธ ํ๋ํ๊ณ ์ถ์ง ์๊ธฐ๋ ํ๊ณ .. ๊ทธ์ ๋๋ก ํ์ํ๋ค๊ณ ์๊ฐํ์ง ์๋๋ค.
๋ณ๋ชฉ์ด ๋ฐ์ํด ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ๊ทธ ๋ถ๋ถ๋ง ํ๋ํ๋ฉด ๊ทธ๋ง!
default batch size๋ ํ๋ฒ์ ๊ฐ์ ธ์ค๋ ๋ ์ฝ๋ ์๋ฅผ ๋ปํ๊ณ
fetch join๋งํผ ๊ฐ๋ ฅํ๊ฒ ํ๋ฐฉ ์ฟผ๋ฆฌ๋ ์๋์ง๋ง
N+1์ 1+1๋ก ํด๊ฒฐํ๋ ๋๋์ด๋ค.
๋ค์๋งํด ์ฐ๊ด๊ด๊ณ๊ฐ x๊ฐ ์์ผ๋ฉด x+1
๋์ ๊ฒฝ์ฐ post์ ์ฐ๊ด๊ด๊ณ๊ฐ image, hashtag, reply
3+1 -> 4๋ฒ์ ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๊ฒ์ด๋ค.
์ค์ ๋ก ํ์ธํด ๋ณด์
14๊ฐ์ post, 3๊ฐ์ ์ด๋ฏธ์ง, 7๊ฐ์ ํด์ํ๊ทธ๋ฅผ ๋ชจ๋ ๋ถ๋ฌ์ค๋๋ฐ ์ฟผ๋ฆฌ ๋ฑ 4๋ฒ์ด๋ฉด ๊ด์ฐฎ์ ๊ฒ ๊ฐ๋ค.
(๋ด๊ฐ ์ค์ ํ 100๊ฐ๋ฅผ ๋์ง ์์ผ๋ฉด ๊ณ์ 4๋ฒ์ผ๋ก ์ ์ง)
select p1_0.id,p1_0.content,p1_0.created_at,p1_0.deleted_at,p1_0.nickname_public,p1_0.title,p1_0.updated_at,p1_0.user_id,p1_0.view_count from post p1_0 where (p1_0.deleted_at IS NULL);
select i1_0.post_id,i1_0.id,i1_0.created_at,i1_0.reply_id,i1_0.storage_url,i1_0.updated_at,i1_0.upload_file_name from image i1_0 where i1_0.post_id in (2,3,4,5,6,7,8,9,10,11,12,13,14,15,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
select ht1_0.post_id,ht1_0.id,ht1_0.tag from hashtag ht1_0 where ht1_0.post_id in (2,3,4,5,6,7,8,9,10,11,12,13,14,15,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
select r1_0.post_id,r1_0.id,r1_0.accepted,r1_0.content,r1_0.created_at,r1_0.parent_id,r1_0.updated_at,r1_0.user_id from reply r1_0 where r1_0.post_id in (2,3,4,5,6,7,8,9,10,11,12,13,14,15,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
** AOP๋ผ๊ณ ํ์ง๋ง Spring AOP๋ฅผ ์ฌ์ฉํ๋ ๊ธฐ์ ์ด ์๋
์๋ต ํฌ๋งท์ ๋ํ ๊ด์ฌ์ฌ์ ๋ํด ๋
ผํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ AOP๋ผ๊ณ ํํ!!
ํ๋ก ํธ์๋์ ํ์
ํด ๋ณธ ๊ฒฝํ์ด ์๋ค๋ฉด,
ํต์ผ๋ ๊ณตํต ์๋ต ํฌ๋งท์ผ๋ก ์ฃผ์ธ์~
๋ผ๋ ์์ฒญ์ ๋ค์ด๋ณธ ์ ์ด ์์ ๊ฒ์ด๋ค.
๋ณดํต ApiResponse(CommonResponse)๋ฅผ ์๋ต์ผ๋ก ์ฌ์ฉํด ์ด๋ฅผ ํด๊ฒฐํ์ํ
๋ฐ,
RestControllerAdvice๋ฅผ ํ์ฉํด ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์๊ฐํด ๋ณด๊ฒ ๋ค!!
์ด๋ฒ์๋ ๊ฐ์์ ์ํฉ์ ์ธ์๋ณด๊ณ ์ข์ ํด๊ฒฐ์ฑ
์ ๋ํด ์๊ฐํด ๋ณด์
์ฐ๋ฆฌ๋ ๋ฐฑ์๋ ํ์ฅ์ด๊ณ
๋ค๋ฅธ ๋ถ์์์ ๊ณตํต ์๋ต ํฌ๋งท ์๊ตฌ์ฌํญ์ด ์์ด ์ด์ฌํ Api Response๋ฅผ ๋ง๋ค์ด
ํ์๋ค์๊ฒ ์ด ํฌ๋งท์ ์ฌ์ฉํด ๋ฌ๋ผ๊ณ ํ๋ค.
ํ์ง๋ง ํ์๋ค์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์๋ค.
@GetMapping("/test")
public String test(){
return "test";
}
@GetMapping("/test2")
public ResponseEntity<String> test2(){
return ResponseEntity.status(OK).body("test2");
}
@GetMapping("/test3")
public ResponseEntity<CommonResponse<String>> test3(){
return ResponseEntity.status(OK).body(new CommonResponse<>());
}๋๊ตฌ๋ String, ๋๊ตฌ๋ ResponseEntity, ๋๊ตฌ๋ ApiResponse๋ฅผ body๋ก..
์ฐ๋ฆฌ๋ ์ด๊ฑธ ์ด๋ป๊ฒ ํด๊ฒฐํด์ผ ํ ๊น?
ํ์๋ค์ ํ๋ช
ํ๋ช
์ฐพ์๊ฐ ์์ ์ ๋ถํํด์ผ ํ ๊น?
์๊ฐ๋ ๋ง์ด ๋ค๊ณ ํ๋ช
ํ๋ช
๋งํ๋ฉฐ ๊ฐ๋ฑ์ด ์๊ธธ ๊ฐ๋ฅ์ฑ์?
๊ทธ๋ฆฌ๊ณ ๋ฐฉ์ฌํ ์ฌ์ด์ ์ฝ๋๊ฐ push ๋์ด ๋ค๋ฅธ ๋ถ์์์ ์ด์ํ ์๋ต์ ์ด๋ฏธ ๋ด๋ฒ๋ ธ๋ค๋ฉด?
์ฑ
์์ ๋๊ฐ ์ ธ์ผ ํ ๊น?
์๋ง ํ์ฅ์ธ ์ฐ๋ฆฌ๊ฐ ์ง๊ฒ ๋ ๊ฒ์ด๋ค..
๋๋ ๊ธฐ์ ์ ์ผ๋ก ์์ ์ฅ์น๋ฅผ ๋ง๋ค์ด
ํ๋ก์ ํธ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ์ด๋๋ ๊ฒ์ด ์ต๊ณ ๋ผ๊ณ ์๊ฐํ๋ค.
์ด์ ์ฝ๋๋ฅผ ๋ณด์
@RestControllerAdvice
public class ResponseInterceptor implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof CommonResponse) {
return body;
}
int status = ((ServletServerHttpResponse) response).getServletResponse().getStatus();
// swagger ์ ์ธ
String path = request.getURI().getPath();
if (path.contains("swagger") || path.contains("api-docs") || path.contains("webjars")) {
return body;
}
// ์กฐ๊ฑด๋ถ ๋ฉ์์ง ์ฒ๋ฆฌ: 2xx -> "Success", ๊ทธ ์ธ -> "Error"
String message = (status >= 200 && status < 300) ? "OK" : "Error";
CommonResponse<Object> commonResponse = new CommonResponse<>();
commonResponse.setStatus(status);
commonResponse.setMessage(message);
commonResponse.setData(body);
// ์๋ต์ String์ผ๋ก ๋ด๋ ๊ฒฝ์ฐ ๋ฐ๋ก ์์ธ์ฒ๋ฆฌ
if (body instanceof String) {
try {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(commonResponse);
} catch (JsonProcessingException e) {
throw new RuntimeException("String response conversion error", e);
}
}
return commonResponse;
}
}์๋ต ์ข
๋ฅ์ ์๊ด ์์ด ์๋ต์ ๊ฐ๋ก์ฑ
๊ณตํต ์๋ต ํฌ๋งท์ผ๋ก ๋ง๋ค์ด์ฃผ๋ ์ฝ๋์ด๋ค.
์ ์ฉ ํ ์คํ๊ฒฐ๊ณผ
์ธ๊ฐ์ง ๋ฐฉ์ ๋ชจ๋ ํต์ผ๋ ์๋ต ํฌ๋งท์ผ๋ก ๋๊ฐ๋ค.
์ด๋ฅผ ์ ์ฉํ ์ง์์ธ ํ๋ก์ ํธ ์๋ต์ ์ด๋ ๊ฒ ์๊ฒผ๋ค
{
"status": 200,
"message": "OK",
"data": [
{
"title": "string",
"content": "string",
"viewCount": 9007199254740991,
"nicknamePublic": true,
"images": [
{
"storageUrl": "string",
"uploadFileName": "string"
}
],
"hashTags": [
{
"tag": "string"
}
],
"replies": [
{
"content": "string",
"accepted": true,
"images": [
{
"storageUrl": "string",
"uploadFileName": "string"
}
],
"replyChildren": [
{
"content": "string"
}
]
}
]
}
]
}์์ธ์ฒ๋ฆฌ๋ ๋ง์ฐฌ๊ฐ์ง
{
"status": 400,
"message": "Error",
"data": {
"path": "/api/post/v1",
"messageDetail": "์์ฒญ ๋ณธ๋ฌธ์ด ๋๋ฝ๋์๊ฑฐ๋ ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค.",
"errorDetail": "Required request body is missing: public org.springframework.http.ResponseEntity<com.ceos21.knowledgein.post.dto.PostDto> com.ceos21.knowledgein.post.controller.PostController.createPost(com.ceos21.knowledgein.post.dto.request.RequestCreatePost,java.lang.Long)"
}
}{
"status": 404,
"message": "Error",
"data": {
"path": "/api/post/v1/123",
"messageDetail": "๊ฒ์๊ธ์ ์ฐพ์ ์ ์์ต๋๋ค.",
"errorDetail": null
}
}
๊ณง ์์ ์์ด๋์์ด์
ํ๋ก์ ํธ๋ฅผ ๊ฐ๋ฐํ ๋
ํ๋ก ํธ์๋์๊ฒ ์ด๋ ๊ฒ ์๋ต์ ๊น๋ํ๊ฒ ์ฃผ๋ฉด ์์ฒญ๋๊ฒ ์ฌ๋ ๋ฐ์ ๊ฒ ๊ฐ์ง ์์๊ฐ?!?
๊ฑฐ๊ธฐ๋ค ์ค์๋ฅผ ๋ฐฉ์งํ ์ ์๋ ์์คํ
์ผ๋ก ๋ฐฑ์๋ ํ์์ ์ฌ๋์ ๋ค
์ฐ๋ฆฌ ๋ชจ๋ ๋ค๋ฅธ ํ์์๊ฒ ์ฌ๋๋ฐ๋ ๊ฐ๋ฐ์๊ฐ ๋์!
Create
ํ์ฌ ์ ๋ก๋ํ ์ด๋ฏธ์ง๋ ๋ก์ปฌ์ ์ ์ฅ, ๋ฒ ํฌํ ๋ ๊ฒฝ๋ก๋ฅผ ๋ฒํท์ผ๋ก ๋ณ๊ฒฝํ๋ฉด ๋จ
Read
Soft Delete
์๊ฐ ๋ถ์กฑ์ด๋ผ๋ ํ๊ณ๋ก ์ธํด ๋ก๊น
+ํ
์คํธ ์ฝ๋ ์์ฑ์ ๋ชปํ๋ค
๋ค์์ ๊ผญ ์์ฑํ๊ฒ ์ต๋๋คใ
ใ
๋ก๊ทธ์ธ์ ํ๋ฉด ์ธ์
์ ์ฅ์์ ์ฌ์ฉ์ ์ธ์
์ ์ ์ฅํ๊ณ
์ธ์
ID๋ฅผ ์ฟ ํค๋ก ํด๋ผ์ด์ธํธ์๊ฒ ์ ์กํ๋ ๋ฐฉ์.
์ฟ ํค๋ ๋ธ๋ผ์ฐ์ ์์์ ์๋ํ๊ณ ์๋์ ์ผ๋ก ์์ฒญ์ ํฌํจ๋๊ธฐ ๋๋ฌธ์ ์์ฒญ ๊ฐํธํ๋ค!!
Spring Security ๊ธฐ๋ณธ ๊ตฌํ ๋ฐฉ์์ด ์ธ์
์ธ์ฆ ๋ฐฉ์์ด๋ค.
ํ์ง๋ง ์ธ์
์ ์ฅ์์ ์ธ์
์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ๋ถํ๊ฐ ์๊ธธ ์ ์๊ณ
์ฟ ํค๋ฅผ ์ฌ์ฉํ์ง ๋ชปํ๋ ํ๊ฒฝ์ด๋ผ๋ฉด?
์ฆ ๋ธ๋ผ์ฐ์ ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋ํ์ง ์๋๋ค๋ฉด?
์ฌ์ฉํ ์ ์๋ค.
ํนํ!! ๋ถ์ฐ ํ๊ฒฝ์์ ๋ชจ๋ ์๋ฒ์ ๋๊ฐ์ ์ธ์
์ ์ ์ฅํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์คํ๋ ค ๊ด๋ฆฌ๊ฐ ํ๋ค๋ค
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋์จ ๊ฒ์ด JWT์ด๋ค.
JWT๊ฐ ๋ฌด์์ธ์ง๋ ์ธํฐ๋ท์ ๋๋ฌด๋๋ฌด ๋ง์ผ๋ ๋์ด๊ฐ๊ณ
์ฒ์ ์ ํ์๋ refresh token, black list, rotate refresh, refresh token ์๋ฒ ์ ์ฅ, refresh token ํด๋ผ์ด์ธํธ์๊ฒ ์ ์ก ๋ฑ๋ฑ..
๋๋ฌด ๋ง์ ๋ด์ฉ๊ณผ ๋ค์ํ ๊ตฌํ ๋ฐฉ์ ๋๋ฌธ์ ํผ๋์ค๋ฌ์ ๋๋ฐ
๊ฒช์๋ ๊ณผ์ ์์์ ๊ณ ๋ฏผํ๋ ๋ด์ฉ๋ค๊ณผ ๊ฐ์ธ์ ์ธ ์๊ฐ์ ์ ์ด ๋ณด๊ฒ ๋ค.
JWT ํ ํฐ์ ๋ฐ๊ธํ๊ณ ๋๋ฉด ์ด ํ ํฐ์ด ์ฐ๋ฆฌ๊ฐ ๋ฐ๊ธํ ๊ฒ์ด ๋ง๋์ง ์ํ์ ์ผ๋ก ๊ฒ์ฆํ๊ณ , ๋ง๋ฃ๋์๋์ง๋ง ๊ฒ์ฌํ๋ค.
์ข ์ด๋ ต๊ฒ ๋งํ๋ฉด Verify Signature ๋ถ๋ถ์ ์ฐ๋ฆฌ์ Secret key๋ก ๋ณตํธํ ํด ์กฐ์๋์๋์ง ๊ฒ์ฆํ๋ ๊ฒ์ด๋ค.
์ด๊ฒ ์ฝ๋์์์ ์ฐ๋ฆฌ๊ฐ ๊ตฌํํ Signature Exception์ ๋ฐ์์ํค๋ ๋ถ๋ถ์ด๋ค.
์ฝ๊ฒ ๋งํ๋ฉด ๋๊ตฌ์๊ฒ ๋ฐ๊ธํ๋์ง ๊ธฐ์ตํ์ง๋ ์๊ณ , ๊ฒ์ฆํ์ง ์๋๋ค๋ ๊ฒ.
๊ทธ๋ผ ๋๊ตฌ์๊ฒ ๋ฐ๊ธํ ํ ํฐ์ธ์ง ํ์ธํ์ง ์๊ธฐ ๋๋ฌธ์ ํ ํฐ์ด ํธ๋ฆฐ๋ค๋ฉด ํด๋น access token์ด ์ ํจํ ์๊ฐ๋์ ๊ณ์ํด์ ๊ณต๊ฒฉ์ ๋ฐ์ ์ ๋ฐ์ ์๋ค.
์ด ํด๊ฒฐ ๋ฐฉ๋ฒ์ผ๋ก access token์ ์ ํจ ์๊ฐ์ ์งง๊ฒ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ด ๊ณ ์๋ ๊ฒ์ด๋ค. (์๋ฅผ๋ค์ด 30๋ถ)
ํ์ง๋ง ์ฌ์ฉ์๋ ์ด ๊ฒฝ์ฐ์ 30๋ถ๋ง๋ค ๋ค์ ๋ก๊ทธ์ธ์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ถํธํ ๊ฒ์ด๋ค.
๊ทธ๋์ ๋์จ ๊ฒ์ด Refresh Token.
Refresh Token์ Access Token์ ๋ฐ๊ธํ ๋ ํจ๊ป ๋ฐ๊ธ๋๋ ํ ํฐ์ด๋ค.
refresh token์ access๋ณด๋ค ์ ํจ๊ธฐ๊ฐ์ด ๊ธธ๊ณ (์๋ฅผ๋ค์ด 1๋ฌ)
์ฌ์ฉ์๊ฐ refresh token์ ๊ฐ์ง๊ณ ์๋ค๋ฉด ๋ง๋ฃ๋ access token์ ์ฌ๋ฐ๊ธ ๋ฐ์ ์ ์๋ค.
์ฆ ๋ค์ ๋ก๊ทธ์ธ ํ์ง ์์๋ refresh token ์ ํจ๊ธฐ๊ฐ ๋์ ์ธ์
์ ์ ์งํ ์ ์๋ ๊ฒ์ด๋ค.
(์ ํํ๋ ์ธ์
์ด ์๋์ง๋ง ์ธ์
์ด๋ผ๊ณ ํํํ๊ฒ ๋ค)
์ฌ๊ธฐ์ refresh token์ ๊ฐ์ง๊ณ ์๋ค๋ฉด -> ์ด ๋ง์ ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๋ฆฌ(์๋ฒ)์๊ฒ ๊ณ์ํด์ ์ ์ก์ ํด์ผ ํ๋ค๋ ๋ป์ด๋
Refresh token์ ์ฟ ํค์ ์ค์ด ์ ์กํ๊ณ , access token์ ํค๋์ ์ค์ด ์ ์กํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ ์ ์๋ค.
(๊ฒ์ํ๋ฉด ์ด ์๋ฃ๋ ์ฐธ ๋ง์ด ๋์๋ ๊ฒ ๊ฐ๋ค)
๊ทธ๋ฐ๋ฐ ์ฒ์์ JWT๊ฐ ๋์จ ๋ฐฐ๊ฒฝ์์ ์ฟ ํค๋ฅผ ์ฌ์ฉํ์ง ๋ชปํ๋ ํ๊ฒฝ์ ํด๊ฒฐํ๊ธฐ ์ํด ๋์
ํ ๊ฒ์ธ๋ฐ,
refresh token์ ์ํด ์ฟ ํค๋ฅผ ์ฌ์ฉํ๋ค๋ ๊ฒ์ด ์กฐ๊ธ ์ด์ํ์ง ์๋? ์๊ฐ์ด ๋ค์๋ค.
๊ทธ๋์ ๊ฐ์ธ์ ์ผ๋ก access, refresh ๋ชจ๋ ํค๋์ ์ค์ด ์ ์กํ๋ ๋ฐฉ์์ด ๋ ํฉ๋ฆฌ์ ์ด๋ผ๊ณ ์๊ฐํ๋ค.
๊ทธ๋ฐ๋ฐ ํ๊ฐ์ง ๋!
Access token์ด ํ์ทจ๋ ๊น๋ด ์ ํจ๊ธฐ๊ฐ์ ์งง๊ฒ ์ค์ ํ ๊ฒ์ธ๋ฐ
refresh token์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ์กํ๋ค๋ฉด ํ์ทจ๋นํ ์ํ์ด ์๋๊ฑฐ ์๋๊ฐ?
๊ทธ๋ผ ์คํ๋ ค ๋ ๊ธด ๊ธฐ๊ฐ๋์ ๊ณต๊ฒฉ๋ฐ๋๊ฑฐ ์๋๊ฐ??
๊ทธ๋์ ๋์จ ๋ฐฉ๋ฒ์ด blacklist, rotate refresh token์ด๋ค.
์ด ๋ฐฉ๋ฒ์ refresh token ํ์ทจ๋ฅผ ๋๋นํด access ํ ํฐ์ ์ฌ๋ฐ๊ธํ ๋๋ง๋ค ์๋ก์ด refresh token์ ๋ฐ๊ธํ๊ณ
๊ธฐ์กด refresh token์ ๋ธ๋๋ฆฌ์คํธ์ ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๋ก์ง์ผ๋ก ์ค๋ช ํ๋ฉด
- access token๊ณผ refresh token์ ๋ฐ๊ธํ๋ค.
- refresh token์ผ๋ก ์ฌ๋ฐ๊ธ ์์ฒญ๋ค์ด์ค๋ฉด
- refresh token์ด ๋ธ๋๋ฆฌ์คํธ์ ์๋์ง ํ์ธ
- ๋ธ๋๋ฆฌ์คํธ์ ์๋ค๋ฉด ์๋ก์ด refresh token๊ณผ access token์ ๋ฐ๊ธ
- ๊ธฐ์กด refresh token์ ๋ธ๋๋ฆฌ์คํธ์ ๋ฑ๋ก
- ์ถ๊ฐ๋ก ๋ก๊ทธ์์์ refresh token์ ๋ธ๋๋ฆฌ์คํธ์ ๋ฑ๋ก
๊ทธ๋ด๋ฏ ํ์ง๋ง?
Refresh Token์ ์ ์ฅํ๋ค๋ ๊ฒ ์์ฒด๊ฐ ์ด์ํ๋ค..
์๊ฐํด๋ณด๋ฉด ํ ํฐ ๊ด๋ จํด์ ์ ์ฅํ์ง ์๋ Stateless ๋ฐฉ์์ด JWT์ ์ฅ์ ์ธ๋ฐ
์ฌ๊ธฐ๋ถํฐ ๊ทธ ์ฅ์ ์ด ์์ด์ง๊ธฐ ์์ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ก์ง๋ ๋ฌด์ง ๋ณต์กํ๋ค
์ด๋ ๊ฒ ํ ํฐ์ ์ ์ฅํ ๊ฑฐ๋ผ๋ฉด ๊ทธ๋ฅ refresh token์ ์๋ฒ์ ์ ์ฅํ๋๊ฒ ๋ซ์ง ์๋?
๊ทธ๋์ refresh token์ ์๋ฒ์์ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด ๋์๋ค.
์ด ๋ฐฉ๋ฒ์ Refresh token์ ํด๋ผ์ธํธ์๊ฒ ๋ณด๋ด์ง ์๊ณ ์๋ฒ์ ์ ์ฅํ๋ ๋ฐฉ๋ฒ์ด๋ค.
์ฌ์ฉ์ id์ refresh token์ ๋งคํํด ์ ์ฅํ๊ณ access token์ ์ฌ๋ฐ๊ธํ ๋ refresh token์ด ์๋์ง ํ์ธํ๋ ๋ฐฉ๋ฒ์ด๋ค.
๋์ ๋ถํ๋ฅผ ์ค์ด๊ธฐ ์ํด refresh token ์ ์ฅ์ Redis๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค.
์ ๋ฐฉ๋ฒ๋ณด๋ค๋ ๊ฐ๋จํ์ง๋ง ์ฌ์ค ์ธ์
์ธ์ฆ ๋ฐฉ์์ด๋ ๋ค๋ฅผ๊ฒ ์๋ค..
๊ฐ์ธ์ ์ผ๋ก ๋ด๋ฆฐ ๊ฒฐ๋ก ์
๋ชจ๋๋ก์ + ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฌ์ฉํ๋ ์น ์๋น๋ ์ธ์
์ธ์ฆ ๋ฐฉ์
๋ถ์ฐ ํ๊ฒฝ or ์ฑ์ ๊ฐ๋ฐํ๋ค๋ฉด JWT๋ฅผ ์ฌ์ฉํ๋๊ฒ ์ ์ ํ๋ค๊ณ ์๊ฐํ๋ค.
ํ์๊ฐ์ ์ ์ง์ ์๋ํฌ์ธํธ๋ฅผ ๋ง๋ค๊ณ OAuth๋ ๊ฐ๋จํ๊ฒ ์ถ๊ฐํ๋ค. ๊ฐ๊ฐ ๊ตฌํํ ๊ฒ์
- Controller, Service
- OAuth dto, OAuth2UserService (ํธ๋ค๋ฌ ์ฌ์ฌ์ฉ)
๋ก๊ทธ์ธ์ UsernamePasswordAuthenticationFilter๋ฅผ ์์๋ฐ์ ๊ตฌํํ๋ค.
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public CustomAuthenticationFilter(final AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
final UsernamePasswordAuthenticationToken authenticationToken;
try {
RequestLogin requestUser = new ObjectMapper().readValue(request.getInputStream(), RequestLogin.class);
authenticationToken = new UsernamePasswordAuthenticationToken(requestUser.email(), requestUser.password());
} catch (IOException e) {
throw new AuthenticationServiceException("Authentication failed", e);
}
setDetails(request, authenticationToken);
return this.getAuthenticationManager().authenticate(authenticationToken);
}
}successfulAuthentication ๋ฉ์๋๋ฅผ ์ง์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ๋ ์๋๋ฐ, ๋๋ ํด๋์ค๋ฅผ ๋ถ๋ฆฌํ๋๊ฑธ ์ข์ํ๊ธฐ๋ ํ๊ณ OAuth success handler์ ๋ก์ง์ด ๋์ผํ๊ธฐ ๋๋ฌธ์ ๋ถ๋ฆฌํ๊ณ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํด ์ฌ์ฌ์ฉํ๋ค.
์ฒ์์ ๋ก๊ทธ์ธ ํ ๋ฐ์ access token์ ๋ณต๋ถ์ ์ฐธ ๋ง์ด ํ๋๋ฐ
์ด๋ฒ์ ์๋ํ ์ํค๋ ๋ฐฉ๋ฒ์ ์๊ฒ ๋์๋ค!.!
Environments์์ ํ๊ฒฝ๋ณ์ ๋ง๋ค์ด ์ฃผ๊ณ
์ปฌ๋ ์ ์์ ํ๊ฒฝ๋ณ์ ์ ํ
ํ ํฐ์ ๋ฐ์์ค๋ ์๋ํฌ์ธํธ (๋ก๊ทธ์ธ)์ script๋ฅผ ์ ์ด์ค๋ค
var token = pm.response.headers.get("access");
pm.environment.set("access", token);๋ง์ง๋ง์ผ๋ก access token์ด ํ์ํ ์๋ํฌ์ธํธ์ ํ๋์ฝ๋ฉํ์ง ๋ง๊ณ {{access}}๋ก ์ ์ด์ค๋ค
์์ฃผ ํธํ๋ค
ํน๋ณํ ๊ฑด ์์๋ค!
docker compose๋ฅผ ์ฌ์ฉํ๋๋ฐ ์ปจํ
์ด๋๋ผ๋ฆฌ ํฌํธ ์ง์ ํด์ฃผ๋ ๋ถ๋ถ์ด ํท๊ฐ๋ ธ์ด์ ์ ๋ฆฌํด๋ดค๋ค.
๋ก์ปฌ์์ db๊ฐ 5432 ํฌํธ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋
๊ฐ์ db ์ปจํ
์ด๋๋ฅผ ๋์ฐ๊ธฐ ์ํด์๋ 5431 ํฌํธ๋ฅผ ์ฌ์ฉํ๊ณ
Redis๋ ๋ง์ฐฌ๊ฐ์ง๋ก 6379๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ 6378๋ก ํฌํธ ํฌ์๋ฉ์ ํด์ฃผ์๋ค.
๊ทธ๋ ๋ค๋ฉด 8080:8080์ผ๋ก ํฌ์๋ฉํ ๋ฐฑ์๋ ์ปจํ
์ด๋๋ ๋์ปค db, ๋์ปค redis๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ ์ด๋ค ํฌํธ๋ฅผ ์ฌ์ฉํด์ผ ํ ๊น?
๊ทธ๋ฆผ์ฒ๋ผ 5432, 6379์ด๋ค.
์ฌ์ค ์๊ฐํด๋ณด๋ฉด ๊ฐ๋จํ๋ฐ ๋์ปค ๋คํธ์ํฌ ์์์ ํต์ ํ๋ ๊ฑฐ๋๊น..
application-prod ์ฒ๋ผ profile์ ๋ถ๋ฆฌํ๋ ๊ฒ์ด๋ผ๊ณ ํ๋ค.
์ด์ ๊ด๋ จํด์
\1. DB ์ปจํ
์ด๋ ์ญ์ 2. nginx ๊ตฌํ 3. Docker hub
3๊ฐ์ง๊ฐ ์ นํ์๋๋ฐ ์ด๊ฒ ๋ฌด์จ ๋ง์ธ์ง ์คํด๋ณด๋ฉด์ ๋ด ์๊ฐ์ ์ ๋ฆฌํด๋ณด๊ฒ ๋ค
- DB ์ปจํ
์ด๋ ์ญ์
AWS๋ก ์๊ฐํด๋ณด๋ฉด ํ๋์ ec2 ์ธ์คํด์ค ์์์ ์ปจํ ์ด๋ 3๊ฐ(spring, db, redis) ๋ชจ๋๊ฐ ๋์๊ฐ๊ณ ์๋ ์ํฉ.
CPU, Memory ์ฌ์ฉ๋์ด ๋จ์๋์ง ์์ ๊ฒ์ด๋ค.
์ฒซ ํ๋ก์ ํธ๋ฅผ ์ด๋ ๊ฒ ํ๋ค๊ฐ ์๋ฒ๊ฐ ๊ณ์ ์ฃฝ์๋ ๊ธฐ์ต์ด ๋๋ค. - Nginx ๊ตฌํ
๋ก๋๋ฐธ๋ฐ์ฑ์ ์ํด ์ฌ์ฉ.
ELB๋ก ๋์ฒดํ ์ ์๋ค๊ณ ์๊ฐํ๋๋ฐ, ์๋ํ๋ ๊ณ์ธต์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ๋๋ค ์ฌ์ฉํ๋๊ฒ ์ข๊ณ ์ผ๋ฐ์ ์ด๋ผ๋ ๊ธ์ ๋ด์ ๋ค์์ ์๊ฐ๊ณผ ๊ฒ์ฆ์ ํด๋ณด๊ฒ ๋ค. - Docker hub
๋์ปค ํ๋ธ์ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ ค ๋ฐฐํฌํ๋ ๋ฐฉ๋ฒ์ด๋ค.
์ด๋ฌ๋ฉด prod ํ๊ฒฝ์์๋ docker hub pull, docker compose up -d, docker image prune -f๋ง ํด์ฃผ๋ฉด ๋๋ค.
์ด๊ฒ๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฒซ ํ๋ก์ ํธ๋ ๋ฐฑ์๋ prod ์ธ์คํด์ค ์์์ git clone, git pull, build image๊น์ง ํ๋ ๊ธฐ์ต์ด ๋๋ค..
์ง๊ธ ์๊ฐํด๋ณด๋ฉด git action์ ์ฌ์ฉํ๋ฉด git action์์์ ๋น๋ํ๊ณ docker hub push ํ๊ฑฐ๋ jenkins๋ฅผ ์ฌ์ฉํ๋ค๋ฉด CI ์๋ฒ์์ ๋น๋, push ํ๋๊ฒ ๋ง๋ค.
์ ๋ฆฌํ๋ฉด, prod ํ๊ฒฝ์์๋ RDS ๊ฐ์ ์ ํ์ ์ฌ์ฉํด DB๋ฅผ ๋ฐ๋ก ๊ด๋ฆฌํ๊ณ
๋ก๋๋ฐธ๋ฐ์ฑ ์ ๊ฒฝ์จ์ฃผ๊ณ
CI ์๋ฒ๋ ๋ฐ๋ก ๋์.
์์ ๋์ปค์ ๊ด๋ จ๋ ๋ด์ฉ์ ์๋์ง๋ง ์์ง์์ค๋ฅผ ๋ณด๋ค๊ฐ ๋ฌธ๋ ์๊ฐ์ด ๋์ ์ ๋ฆฌํด๋ดค์ต๋๋ค
๊ตฌ๊ธ์ ๊ฒ์ํ๋ฉด ์ด๋ฐ ๊ทธ๋ฆผ์ด ๋์ค๋๋ฐ,
Web Server๋ ์ ์ ์์์ ์ ๊ณตํ๊ณ , WAS๋ ๋์ ์์์ ์ ๊ณตํ๋ค๊ณ ๋์ ์๋ค.
์ ์ ์์์ HTML, CSS, JS, ์ด๋ฏธ์ง ๋ฑ๋ฑ
๊ทธ๋์ Webserver์ ์์๋ก Nginx, Apache๊ฐ ๋์์๊ณ
WAS์ ์์๋ก Tomcat, Jetty ๋ฑ์ด ์๋ค๊ณ ํ๋ค.
์ฝ๊ฐ ์๋ฟ์ง ์๋๋ฐ, ๊ณ์ฐ์ด ๋ค์ด๊ฐ๋ ๋ฑ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๋ ๋์ WAS์ด๊ณ
WAS๋ WebServer๊ฐ ํ๋ ์ผ์ ์ํํ ์ ์์ง๋ง
๊ณ ๊ธ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ WAS ์์์ด ์๊น์ฐ๋ ์๋น๋ง ํ๋ฉด ๋๋ ์ผ์ WebServer์๊ฒ ์ํค์๋ ๊ฒ์ผ๋ก ์๊ฐํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
์ฒ์ ์ด๋ฅผ ๋ณด๊ณ ๋ ์๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ๊ฐ๋ฐํ๋๊ฑด Spring์ธ๋ฐ ์ WAS๊ฐ tomcat์ด์ง? ์๋ค
์ฌ์ค ๋๋ฌด ๋น์ฐํ๋ค๊ณ ์๊ฐํ๋ ๋ถ๋ค์ด ์์ ์ ์์ง๋ง ใ
ใ
์ด๋ถ๋ถ์ ๊น์ํ๋์ ์คํ๋ง ๋ถํธ ๊ฐ์๋ฅผ ๋ฃ๊ณ ์กฐ๊ธ ์ดํด๊ฐ ๋์๋ค. - ์ด ๊ฐ์ ์ถ์ฒ (ํ๋ฐ๋ถ์๋ ์คํ๋ง ์ก์ถ์์ดํฐ, ๊ทธ๋ผํ๋, ํ๋ก๋ฉํ
์ฐ์ค ๋ฑ ๋ชจ๋ํฐ๋ง ๋ด์ฉ๋ ๋์ด!)
์ฌ๊ธฐ์ ์ง์ war๋ก ๋น๋ํด ํฐ์บฃ ์๋ฒ์ ์คํ๋ง ํ์ผ์ ์ฌ๋ฆฌ๋ ๋ฐฉ์์ผ๋ก ๋ฐฐํฌํ๋ ๊ฒฝํ์ ํด๋ดค๋ค.
๊ทธ๋ฌ๊ณ ๋๋ ํฐ์บฃ์ด ๋ด์ฅ๋์ด ์๋ค, ์ฆ ๋ด์ฅ ์๋ฒ๊ฐ ์๋ค๋ผ๋ ๋ง๋๋ฌธ์ ์คํ๋ง ์์ ํฐ์บฃ์ด ์๋ค๊ณ ์ฐฉ๊ฐํ๋ค๋ ์ฌ์ค์ ์๊ฒ ๋์๋ค.
๊ทธ๋ฅ Tomcat ์ํํธ์จ์ด ์์กด์ฑ์ ํฌํจํ๊ณ ์๋๊ฑฐ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ ๋ฏ ํ๋ค.
๊ฐ์์์๋ ์ธ์ฅ ํฐ์บฃ ์ค์น
์๋ธ๋ฆฟ ์ปจํ
์ด๋ ์ด๊ธฐํ๋ฅผ ์ฌ์ฉํด ํ์ํ ์๋ธ๋ฆฟ์ ๋ฑ๋ก
์คํ๋ง ์ปจํ
์ด๋๋ฅผ ์์ฑํด์ ๋ฑ๋ก
์คํ๋ง MVC๊ฐ ๋์ํ๋๋ก DispatcherServlet์ ๋ฑ๋ก
ํ๋ฆ์ผ๋ก ์งํ๋๋ค.
๋ด์ฅ ํฐ์บฃ์
์ ๊ณผ์ ๋ค์ ๋๊ฐ์ง๋ง ํฐ์บฃ์ ์์กด์ฑ์ผ๋ก ๊ด๋ฆฌ
์ด๋ฅผ jar ํ์ผ๋ก ๊ด๋ฆฌํ๋๋ฐ, jar(spring) ๋ด์ jar(tomcat)์ ํฌํจํ ์ ์๊ธฐ๋๋ฌธ์ Fatjar ์ฌ์ฉ
๋ด์ฅ ํฐ์บฃ ์คํ, ์คํ๋ง ์ปจํ
์ด๋ ์์ฑ, ๋์คํจ์ฒ ์๋ธ๋ฆฟ ๋ฑ๋ก๋ฑ ๋ชจ๋ ๊ณผ์ ์ ์ฒ๋ฆฌํด ์ฃผ๋ ๋ถํธ ํด๋์ค๋ฅผ ๋ง๋ฌ
์ด๊ฒ์ด ๋ฐ๋ก Spring Boot
๋น ๋ฅด๊ฒ ๊ฐ์๋ฅผ ๋ณด๋ฉฐ ์ฌ๊ธฐ๊น์ง ์ดํดํ์๋๋ฐ, ์ด๋ฒ ๊ธฐํ๋ก ์ด๋ก ์ ์ธ ๋ด์ฉ์ ์กฐ๊ธ ๋ ํ๋ณด๊ฒ ๋ค
ํนํ ์๋ธ๋ฆฟ์ ๋ฑ๋กํ๋ค๋๊ฒ ๋ญ์ง, DispatcherServlet์ด ๋ญ์ง
HTTP ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด Servlet ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ์๋ธ๋ฆฟ ์ปจํ
์ด๋์ ๋ฑ๋กํด์ผ ์ ์์ ์ผ๋ก ์๋ํ๋ค.
์๋ ์๋ ์ด๋ฅผ web.xml ํ์ผ์ ์ง์ ๋ฑ๋กํ๋ค.
์ฆ HTTP ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํ๋์ ๋ฐฉ์์ธ ๊ฒ์ด๋ค.
ํ์ง๋ง!! Spring MVC๊ฐ ๋ฑ์ฅํ๋ฉด์ ์์ฒญ์ Controller ๋จ์์ ์ฒ๋ฆฌํ๊ธฐ ์์ํ๊ณ DispatcherServlet์ด ๋ฑ์ฅํ๋ค.
DispatcherServlet์ Front Controller ํจํด์ ์ฌ์ฉํด HTTP ์์ฒญ์ ์ฒ๋ฆฌํ๋ ์๋ธ๋ฆฟ์ด๋ค.
์ฝ๊ฒ ๋งํด ๋์คํจ์ฒ ์๋ธ๋ฆฟ์ ํธ๋ค๋ฌ ๋งคํ ์ ๋ณด๋ฅผ ์ค์บํด ์์ฒญ์ ์ ์ ํ ์๋ธ๋ฆฟ OR ์ปจํธ๋กค๋ฌ๋ฅผ ํธ์ถํด ์ฃผ๋ ๊ฒ.
์ฆ ์ฐ๋ฆฌ๋ ์๋ธ๋ฆฟ์ ๋ฑ๋กํ ํ์ ์์ด @Controller๋ก ์คํ๋ง ๋น์ ๋ฑ๋กํด ๋์ผ๋ฉด
๋์คํจ์ฒ ์๋ธ๋ฆฟ์ด ํด๋น ์ปจํธ๋กค๋ฌ๋ฅผ ํธ์ถํด์ฃผ๋ ๊ฒ์ด๋ค.
์ด ๊ทธ๋ฆผ์ด ๊ฐ์ฅ ์ดํด๊ฐ ์ ๊ฐ๋ ๊ฒ ๊ฐ๋ค.
์์ฒญ์ ์ฒ๋ฆฌํ ์ปจํธ๋กค๋ฌ๋ฅผ ์ฐพ์์ ์์(ํธ์ถ) ํ๋ค๋ ๊ฒ์ด ๋ญ๊น?
๊ณผ์ ์ ์ด๋ ๋ค๊ณ ํ๋ค.
- ํด๋ผ์ด์ธํธ ์์ฒญ์ ๋์คํจ์ฒ ์๋ธ๋ฆฟ์ด ๋ฐ์
- ์์ํ ์ปจํธ๋กค๋ฌ๋ฅผ ์ฐพ์
- ์์ฒญ์ ์ปจํธ๋กค๋ฌ๋ก ์์ํ ํธ๋ค๋ฌ ์ด๋ํฐ๋ฅผ ์ฐพ์ ์ ๋ฌ
- ํธ๋ค๋ฌ ์ด๋ํฐ๊ฐ ์ปจํธ๋กค๋ฌ๋ก ์์ฒญ์ ์์
- ๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ
- ์ปจํธ๋กค๋ฌ๊ฐ ๋ฐํ๊ฐ์ ๋ฐํ
- ํธ๋ค๋ฌ ์ด๋ํฐ๊ฐ ๋ฐํ๊ฐ ์ฒ๋ฆฌ
- ์๋ฒ ์๋ต์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํ
๊ฐ์ ์์๋ก ์ข๋ ์์ธํ๊ฒ ๋ณด์ (๋ณด์ค ๋ถ๋ค์ ์ํด : tomcat-embed-core ํจํค์ง์ ์์ต๋๋ค)
-
ํด๋ผ์ด์ธํธ ์์ฒญ์ ๋์คํจ์ฒ ์๋ธ๋ฆฟ์ด ๋ฐ์
1-1. request, response๋ฅผ ์บ์คํ1-2. method์ ๋ฐ๋ผ ์งํ
์ฌ๋ฐ๋ ์ ์ 2022๋ ์ ์์ฑ๋ ๋ง๋๋ ๊ฐ๋ฐ์ ๋ธ๋ก๊ทธ์๋ PATCH ๋ฉ์๋๊ฐ ๋ฆ๊ฒ ๋์์ ๋ฐ์ํ ์์ธ์ฒ๋ฆฌ์ ๋ํ ๋ด์ฉ์ด ๋์์๋๋ฐ, ์ง๊ธ์ ์ ๊ทธ๋ ์ด๋ ๋ ๊ฒ ๊ฐ๋ค. HTTP_SERVLET_METHODS ์์ PATCH ๋ฉ์๋๋ ๋ค์ด๊ฐ ์๋ค.
1-3. ๋ด๋ถ์ ์๋ processRequest ๋ฉ์๋
๋๋์ด DispatcherServlet์ด ๋ฑ์ฅํ๋ค. processRequest ๋ฉ์๋์์ do service ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ์๋๋ฐ, ์ด๋ ์ถ์ ๋ฉ์๋๋ก DispatcherServlet์ ๊ตฌํ๋์ด ์๋ค.doService ๋ฉ์๋ ๋ด๋ถ์์๋ ์ฑ๊ณต ๋ก์ง์ธ doDispatch๊ฐ ํต์ฌ์ด๋ฏ๋ก ๋ฐ๋ก ๋์ด๊ฐ์
-
์์ํ ์ปจํธ๋กค๋ฌ๋ฅผ ์ฐพ์
-
์์ฒญ์ ์ปจํธ๋กค๋ฌ๋ก ์์ํ ํธ๋ค๋ฌ ์ด๋ํฐ๋ฅผ ์ฐพ์ ์ ๋ฌ
2, 3 -1. doDispatch ๋ฉ์๋ ๋ด๋ถ์์ ํธ๋ค๋ฌ ์ด๋ํฐ๋ฅผ ์ฐพ์
(์ด ๋ถ๋ถ ๋ด๋ถ๋ ์๋ต ํ๊ฒ ๋ค..)
-
ํธ๋ค๋ฌ ์ด๋ํฐ๊ฐ ์ปจํธ๋กค๋ฌ๋ก ์์ฒญ์ ์์
4-1. ํธ๋ค๋ฌ ์ด๋ํฐ๋ฅผ ํตํด ์ปจํธ๋กค๋ฌ ๋ฉ์๋๋ฅผ ํธ์ถ4-2. ๊ตฌํ์ฒด์ธ AbstractHandlerMethodAdapter์ handle ๋ฉ์๋ ๋ด๋ถ๋ฅผ ๋ณด๋ฉด
๋ฐํ ํ์ ์ด ModelAndView์ธ๋ฐ MVC์ ๋์๊ฐ ๋๋๊ฒ ๋๊ปด์ง๋๊ฐ?
-
๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ
-
์ปจํธ๋กค๋ฌ๊ฐ ๋ฐํ๊ฐ์ ๋ฐํ
-
ํธ๋ค๋ฌ ์ด๋ํฐ๊ฐ ๋ฐํ๊ฐ ์ฒ๋ฆฌ
-
์๋ฒ ์๋ต์ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํ
4๋ฒ ์ดํ ๊ณผ์ ๋ค์ ๋ฐ๋ผ๊ฐ๋ณด์ง ๋ชปํ๋๋ฐ, ์๋ง ์ญ์์ด๋ผ ๋น์ทํ์ง ์์๊น ์ถ๋ค.
์ด๋ ต๋ค์ ํํ ์๋ชป๋ ๋ด์ฉ ์์ผ๋ฉด ์ง์ ํด์ฃผ์ธ์!!
์ด๋ ๊ฒ ํ๋ฆ์ ๋ฐ๋ผ๊ฐ ๋ณด๋ ๋์คํจ์ฒ ์๋ธ๋ฆฟ์ด ๋ญ์ง ํท๊ฐ๋ฆฌ์ง๋ ์์ ๊ฒ ๊ฐ๋ค.
GPT์๊ฒ ์คํ๋ง ์ปจํ
์ด๋์์ ๊ด๋ฆฌํ๋๊ฑฐ๋.. ๋น์ด๋ ๋ฌผ์ด๋ดค๋ ์ง๋๋ ์ ์๋
~
๋๋ฌด ๋๋ฌด ์ข์๋ ์ฐธ๊ณ ์๋ฃ (๋ง๋๋ ๊ฐ๋ฐ์ ์ต๊ณ ! ์์ ์ถ์ฒ)
https://velog.io/@ddangle/Spring-Servlet-%EA%B3%BC-Servlet-Container
https://mangkyu.tistory.com/216
์ถ๊ฐ๋ก ์ ๋ฒ ์ฝ๋๋ฆฌ๋ทฐ๋ exception filter๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ๊ตฌํํด์ผ ํ๋ ์ด์ ๋ฅผ ์๊ฐํด๋ณด๋ผ๋ ๋๊ธ์ ๋ณด๊ณ ๋ต๋ณ์ด ๋ฐ๋ก ์๊ฐ์ด ์๋์.. ๋ฐ์ฑ์ ์๋ฏธ๋ก ๋ค์ ์ฐพ์๋ดค๋ค. ๋ฒจ๋ก๊ทธ ๋ก๊ธ์ด๋์ด ์ ๋ฆฌ๋ฅผ ์ฐธ ์ํด๋์ผ์
จ๋ค.
https://velog.io/@ddangle/Spring-Filter-vs-Interceptor
services:
cache:
container_name: redis
image: redis:alpine
ports:
- 6379:6379
volumes:
- redis_data:/data
networks:
- knowledge_network
application:
container_name: knowledge_be
image: knowledge_be
ports:
- 8080:8080
depends_on:
- cache
env_file:
- .env
networks:
- knowledge_network
volumes:
redis_data:
networks:
knowledge_network:
driver: bridge'ACCESS_TOKEN_EXPIRE_TIME': Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'; For input string: "1_800_000"yml๊ณผ ๋ค๋ฅด๊ฒ .env์์๋ 100_000 ์ด๋ ๊ฒ ๊ตฌ๋ถ์ด ์๋๋ค๊ณ ํ๋ค
ACCESS_TOKEN_EXPIRE_TINE=100000
์ด๋ ๊ฒ ์์
์ด์ธ์ ๋ณ๋ค๋ฅธ ์๋ฌ๋ ์์๋ค
Redis๋ elastic cache๋ฅผ ์ฌ์ฉํ๋ฉด ์ข์ง๋ง ์ด๋ฒ ๊ณผ์ ๋ docker container๋ก ๋์ฒดํ๋ค
ec2 docker be container ๋ด๋ถ ๋ก๊ทธ
๊ฐ๋จํ ํ๋ก์ ํธ์ฌ์ ์ด๋ ๊ฒ ํ์ง๋ง ์ข์ ๊ตฌ์กฐ๋ ์๋๋ค
๋ค๋ฅธ ํ๋ก์ ํธ ์ํคํ
์ณ๋ ์ด๋ ๊ฒ ์ค๊ณํ๋ค
๊ฐ์ ์ ์ ec2๋ ํ๋ผ์ด๋น ์๋ธ๋ท์ ์๋๊ฒ ์ข๊ณ ๊ทธ๋ฌ๋ ค๋ฉด nat gateway๊ฐ ํ์ํ๋ค.

์ด๋ฐ๋๋
์ด๋ฒ์ ์๋ก ํด๋ด์ ์์ ํ ์ดํดํ์ง ๋ชปํ์ง๋ง
๊ฐ์ฉ์์ญ ๋๊ฐ์ ๊ฑธ์ณ ์๋ ์ด์ ๋ ํฌ๊ฒ ๋๊ฐ์ง ์ด์ ์ธ๋ฐ
- ์ฅ์ ๋ฐ์์ ๊ฐ์ฉ์์ญ์ ๋ฐ๊ฟ์ ์๋น์ค๋ฅผ ๊ณ์ ์ ๊ณตํ๊ธฐ ์ํด
- ๋ก๋๋ฐธ๋ฐ์ฑ์ ์ํด(๋ถํ ๋ถ์ฐ)
์ถ๊ฐ ์ค๋ช
์ผ๋ก
rds, elastic cache ๋ถ๋ถ์ ์ธ์คํด์ค ์ฌ๋ฌ๊ฐ ๋ ์๋๊ฒ
๋ง์ด ๋ดค์ ๋ค์คAZ ์ต์
์ด๋ค
bootjar ํ์คํฌ๋ ์คํ๊ฐ๋ฅํ jar ํ์ผ์ ๋ง๋ ๋ค.
jarํ์๋
jar ํ์คํฌ๋ ํด๋์ค ํ์ผ์ ๋ง๋ ๋ค.
buildํ์๋
Buildํ ๋ plain jar, jarํ์ผ 2๊ฐ๊ฐ ์์ฑ๋๋๋ฐ
๊ฐ๊ฐ ์์์ ๋ณธ jar, bootjar ํ์คํฌ๋ฅผ ์คํํ์๋์ ํ์ผ์ด๋ค.
๋ฐฐํฌ์์๋ ๋ณดํต ์คํ ๊ฐ๋ฅํ ํ์ผ์ด ํ์ํ๊ธฐ ๋๋ฌธ์ bootjar ํ์๋ (plain ์๋ jar) ํ์ผ์ ๋ง์ด ์ฌ์ฉํ๋ค.
๋ญ์ง ๋์ถฉ ์์์ผ๋๊น build๋ bootjar๊ฐ ๋ญ๊ฐ ๋ค๋ฅธ์ง ๋ณด์
์คํ 10:25:44: Executing 'bootJar'...
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :resolveMainClassName UP-TO-DATE
> Task :bootJar UP-TO-DATE
BUILD SUCCESSFUL in 518ms
4 actionable tasks: 4 up-to-date
์คํ 10:25:44: Execution finished 'bootJar'.

์ปดํ์ผ์ ๋ง๊ทธ๋๋ก ๋ฐ์ดํธ์ฝ๋๋ก ์ปดํ์ผํ๊ฑฐ๊ณ
processResources๋ ๋ฆฌ์์ค ํ์ผ์ ๋ณต์ฌํ๋ ์์
์ด๋ค.
application.yml๊ฐ์ ํ์ผ ๋งํ๋ ๊ฒ ๊ฐ๋ค. -> ์ด๋ถ๋ถ ์ ๋ฒ ์ธ์
์๊ฐ์ ์ง๋ฌธํ๋ ๋ถ๋ถ!!! ๋ฐ๊ฒฌํ๋ค ๋ค์์ ์ ๋ฆฌํด๋ณด๊ฒ ๋ค
classes๋ ์์ ๋ ๋จ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์๋๋ ๋จ๊ณ
์คํ 10:24:48: Executing 'build'...
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :resolveMainClassName UP-TO-DATE
> Task :bootJar UP-TO-DATE
> Task :jar UP-TO-DATE
> Task :assemble UP-TO-DATE
> Task :compileTestJava NO-SOURCE
> Task :processTestResources
> Task :testClasses
> Task :test NO-SOURCE
> Task :check UP-TO-DATE
> Task :build UP-TO-DATE
BUILD SUCCESSFUL in 490ms
6 actionable tasks: 1 executed, 5 up-to-date
์คํ 10:24:48: Execution finished 'build'.build ํ์คํฌ์์ bootjar, jar ํ์คํฌ ํฌํจํ๋๊ฑฐ ๋ง๊ณ ๋ค๋ฅธ์ ์
- assemble
jar/bootjar ๊ฐ์ ํจํค์ง ํ์คํฌ๋ฅผ ๋ฌถ์ด๋๋ ํ์คํฌ๋ผ๊ณ ํ๋๋ฐ ๋ญ๋ง์ธ์ง ์ ๋ชจ๋ฅด๊ฒ ๋ค - compileTestJava
ํ ์คํธ ์ฝ๋๋ฅผ ์ปดํ์ผํ๋ ํ์คํฌ - processTestResources
ํ ์คํธ ๋ฆฌ์์ค ํ์ผ์ ๋ณต์ฌํ๋ ํ์คํฌ (src/test/resources -> build/resources/test) - testClasses
ํ ์คํธ ์คํ ์ค๋น๋ฅผ ๋ง๋ฌด๋ฆฌํ๋ ํ์คํฌ - test
ํ ์คํธ๋ฅผ ์คํ - check
code quality๋ฅผ ๊ฒ์ฌํ๋ ํ์คํฌ๋ผ๊ณ ํ๋๋ฐ, jacocoTestReport -> ์น์ํ ์ด๋ฆ์ด ๋ณด์๋ค.
์ ํ๋ก์ ํธ์์ CI๊ณผ์ ์ ์ปค๋ฒ๋ฆฌ์ง ํ ์คํธ์ ์ฝ๋ ํ์ง๊ฒ์ฌ๋ฅผ ํฌํจ์์ผ์ sonar cloud๋ฅผ ์ฌ์ฉํ์๋๋ฐ
์ด๋ jacoco๊ฐ ์ฌ์ฉ๋์๋ค.. ์ด๊ฒ๋ ๋ค์์ ์ข๋ ํ๋ณด์ - build
assemble, check ํ์คํฌ๋ฅผ ํฌํจํ๋ค. ์ต์ข ์ ์ผ๋ก build๋ฅผ ์ํ(๋ฐ์ดํธ์ฝ๋ ์์ฑ).
์ค์ ๋ก ์์ถ์ ํ์ด๋ณด๋ฉด
์ฐธ๊ณ ์๋ฃ
https://www.devkuma.com/docs/gradle/bootjar-jar/
include ๋ถ๋ถ์ ์ฃผ์์ฒ๋ฆฌํ๊ณ jarํ์ผ์ ๋ง๋ค์ด๋
์ฐ๋ คํ๋๋ก ๋น๋ฐ๊ฐ๋ค์ด ๋ค ๋ค์ด๊ฐ์๋ค.
๋จ, ์ ํ๊ฒฝ๋ณ์๋ค์ ${} ๋ถ๋ถ์ ์ฃผ์
(bake) ํ์ง๋ ์๋๋ค๊ณ ํ๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ docker compose์์ .env ํ์ผ์ ์ฌ์ฉํด ์ฃผ์
ํ๋๊ฒ ์ฐ์ ์์๋ฅผ ๊ฐ์ง๋ ๊ฒ ๊ฐ๋ค.
๊ฒฐ๋ก ์ docker image๋ฅผ ํผ๋ธ๋ฆญ์ผ๋ก ์ฌ๋ฆด๊ฑฐ๋ฉด application-prod ๊ฐ์ ํ์ผ์ ํ๊ฒฝ๋ณ์๋ฅผ ๋ฃ์ง ๋ง๊ณ
.env ํ์ผ์ ์ฌ์ฉํ๋๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค.
์ด๋ฒ ์ง์์ธ์์ ์ฌ์ฉํ env ํ์ผ ์์
POSTGRESQL_USERNAME=postgres
POSTGRESQL_PASSWORD=XXXXXXXXXX
POSTGRESQL_HOST=ceos-knowledge-db.XXXXXXXXX.ap-northeast-2.rds.amazonaws.com
POSTGRESQL_PORT=5432
POSTGRESQL_DATABASE=knowledge
BASE_URL=ec2-X-X-X-X-X.ap-northeast-2.compute.amazonaws.com
FRONT_URL=http://localhost:3000
REDIS_HOST=redis
REDIS_PORT=6379
DIR=/Users/leo/knowledgein_images/
# access - 30๋ถ
ACCESS_EXPIRATION=1800000
# refresh - 30์ผ
REFRESH_EXPIRATION=2592000000
JWT_SECRET="eawfiwsfefafsdljlewfw4ennnsd412asdffalskfwnld123aiiiewnsfdwenwekewnlewnklenwlkewlewnlwewenl3101ln4223jndsfnfl1sdfwfeljk134"
GOOGLE_CLIENT="ํด๋ผ์ด์ธํธ id"
GOOGLE_SECRET="ํด๋ผ์ด์ธํธ ์ํฌ๋ฆฟ"์ถ๊ฐ์ ์ผ๋ก!!
profiles.active๋ฅผ ์ฌ์ฉํ์ง ์์๋๋ฐ ์ด๊ฑธ ์ฌ์ฉํด์ผ ๋น๋์์๋ ๋ค๋ฅธ ํ๋กํ ํ๊ฒฝ๋ณ์๋ ํฌํจ๋์ง ์๋๋ก ์๋ํ๋ค๊ณ ํ๋ค.
์ฝ๋๊ฐ ์ข ๋ง์์ ์๋ค์ด์ include๋ก ๋งจ๋ ํ๋๋ฐ ๋ค์๋ฒ์ ์ ์ฉํด๋ด์ผ๊ฒ ๋ค







































