Skip to content
ย 
ย 

Latest commit

ย 

History

48 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

spring-knowledgeIn-21st

ceos back-end 21st naver knowledge-in clone coding project

์„ค๊ณ„

DB์™€ JPA๋ฅผ ์งฌ๋ฝ•์‹œํ‚ค๋ฉฐ ์ƒ๊ฐ ์ •๋ฆฌํ•œ ๊ทธ๋ฆผ

Image

๊ทธ๋ฆผ์—์„œ ์‹ซ์–ด์š”/์ข‹์•„์š” ์—ฌ๋ถ€๋Š”
boolean์œผ๋กœ ๊ทธ๋ ธ๋Š”๋ฐ
๊ตฌํ˜„ํ•˜๋ฉด์„œ Enum์œผ๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

๋นจ๊ฐ„์ƒ‰ ๊ธ€์”จ๋กœ ์ ์€ ๋ถ€๋ถ„์€ cascade ๊ด€๋ จ์ธ๋ฐ
๋ฐ‘์—์„œ ์‚ดํŽด๋ณด์ž.

๊ณผ์ œ์— ์ž‘์„ฑ๋œ ๊ฒƒ ์™ธ์— ๋””ํ…Œ์ผ์€

  • ์ƒ์„ฑ, ์—…๋ฐ์ดํŠธ ์‹œ๊ฐ„ ๊ธฐ๋ก (BaseTimeEntity)
  • ์กฐํšŒ์ˆ˜
  • ๋‹‰๋„ค์ž„ ๊ณต๊ฐœ / ๋น„๊ณต๊ฐœ
  • ์ฑ„ํƒ ์—ฌ๋ถ€
  • ๋Œ€๋Œ“๊ธ€ ์ž๊ธฐ ์ฐธ์กฐ
  • ์‚ฌ์ง„ ์—…๋กœ๋“œ๋ช…, storage๋ช… ๋ถ„๋ฆฌ
  • cascade
Image

๊ฐœ์ธ์ ์œผ๋กœ ์ข€ ๋ค๋ฒ™๋Œ€๊ณ  ์ž˜ ๊นŒ๋จน์–ด์„œ..
๊ทธ๋ฆผ์œผ๋กœ ๊ทธ๋ฆฌ๋Š”๊ฒŒ ์ตœ๊ณ 

๋„ค์ด๋ฒ„ ์ง€์‹์ธ

Image
  • ์ œ๋ชฉ
  • ๋‚ด์šฉ
  • ์ž‘์„ฑ์ž๋ช… ๊ณต๊ฐœ/๋น„๊ณต๊ฐœ
  • ์ž‘์„ฑ์ผ
  • ํ•ด์‹œํƒœ๊ทธ
  • ์กฐํšŒ์ˆ˜
Image
  • ๋Œ“๊ธ€
  • ๋Œ€๋Œ“๊ธ€
  • ์‹ซ์–ด์š”/์ข‹์•„์š”
  • ์ž‘์„ฑ์ผ

Cascade

์ด๋ฒˆ ๊ณผ์ œ์—์„œ ์ฃผ์š” ๊ณ ๋ฏผ ํฌ์ธํŠธ!

Image Image

Remove

ํ•˜๋‚˜์”ฉ ๋ณด๋ฉด

์œ ์ € ํƒˆํ‡ด - 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();
        });
    }

Persist

๊ธ€์„ ์ž‘์„ฑํ•˜๋ฉด ๋Œ€๋ถ€๋ถ„ ํ•ด์‹œํƒœ๊ทธ๋‚˜ ์‚ฌ์ง„์„ ๊ฐ™์ด ์—…๋กœ๋“œํ•œ๋‹ค.
์ด๊ฑด ๋ฐ”๋กœ ์ฝ”๋“œ๋ฅผ ๋ณด์ž

    @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 ํ–ˆ๋Š”๋ฐ?

Image Image

insert ์ฟผ๋ฆฌ๊ฐ€ ์ž˜ ๋‚˜๊ฐ€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
cascade ์•ˆ๊ฑธ์–ด์ฃผ๋ฉด
post image ํ•˜๋‚˜ ํ•˜๋‚˜ save save save ํ•ด์•ผํ•œ๋‹ค.

๊ฒฐ๊ณผ

Comment๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์˜ˆ์•ฝ์–ด๋กœ ๊ฑธ๋ ค ์žˆ๋‹ค๊ณ  ํ•ด์„œ
Reply๋กœ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ–ˆ๋‹ค

Image

์ปค๋ฒ„๋ฆฌ์ง€

Image

๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ - ์‹ค๋ฌด

์ง์ ‘ ๊ฒฝํ—˜์„ ํ•œ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ
์ด์•ผ๊ธฐ๋ฅผ ์กฐ๊ธˆ ๋“ค์–ด๋ณด๋ฉด ์‹ค๋ฌด๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ณต๋ถ€ํ•˜๋Š” ๊ฒƒ๊ณผ ์กฐ๊ธˆ ๋‹ค๋ฅด๋‹ค๊ณ  ํ•œ๋‹ค.

ํŠนํžˆ JPA ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘์ด๋‚˜ DB foreign key, cascade ์„ค๊ณ„ ๋ถ€๋ถ„

Image
fk๊ฐ€ ๊ฑธ๋ ค ์žˆ์œผ๋ฉด ํ…Œ์ด๋ธ” ์‚ญ์ œ, ์ด๋™์ด ํž˜๋“ค๋‹ค. (์•„๋งˆ ๊ฒฝํ—˜์ด ์žˆ์„ ๊ฒƒ)
๋˜ํ•œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž˜๋ชป ์‚ญ์ œ๋˜๊ฑฐ๋‚˜
์ •ํ•ฉ์„ฑ์ด ๊นจ์ง€๋Š” ์ด์œ ๋กœ fk๋ฅผ ์ž˜ ์žก์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ
JPA๋ฅผ ์‚ฌ์šฉํ• ๋•Œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์ž˜ ์žก์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™๋‹ค.

์ด๋ฒˆ ๊ณผ์ œ์—์„œ JPA๋ฅผ ๊ณต๋ถ€ํ•ด๋ณด๊ธฐ ์œ„ํ•ด
์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋ชจ๋‘ ์žก์•˜์ง€๋งŒ, ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค. ๋ฌด์—‡๋ณด๋‹ค ์—ฐ๊ด€๊ด€๊ณ„๋กœ ์ŠคํŠธ๋ ˆ์Šค ์•ˆ๋ฐ›๊ณ  ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‚˜์˜ ๊ฒฝ์šฐ ๋А์Šจํ•œ ๊ฒฐํ•ฉ (DB) + ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘ (JPA)
๋ฐฉ๋ฒ•์„ ์• ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

Image

์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์—†๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ด์ง€๋งŒ
pk๊ฐ€ ์•„๋‹Œ ์‹๋ณ„์ž๋ฅผ ๋งŒ๋“ค์–ด์„œ
ํ•ด๋‹น ์ปฌ๋Ÿผ์„ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•ด๋„ Data Grip์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด
virtual foreign key๋ฅผ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์—
๋‹ค์ด์–ด ๊ทธ๋žจ์œผ๋กœ ์ฐธ์กฐ ๊ด€๊ณ„๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ธํ…”๋ฆฌ์ œ์ด์—์„œ ์ด๋Ÿฐ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด ๋†“์€๊ฒƒ์ด
๋งŽ์ด๋“ค ์ด๋ ‡๊ฒŒ ํ•œ๋‹ค๋Š” ์ฆ๊ฑฐ๊ฐ€ ์•„๋‹๊นŒ?

Image

Image

PUT VS PATCH

https://mangkyu.tistory.com/251

Path variable VS Query parameter

DELETE : Soft delete & Response

https://hongong.hanbit.co.kr/http-%EC%83%81%ED%83%9C-%EC%BD%94%EB%93%9C-%ED%91%9C-1xx-5xx-%EC%A0%84%EC%B2%B4-%EC%9A%94%EC%95%BD-%EC%A0%95%EB%A6%AC/

DTO ๋ถˆ๋ณ€์„ฑ feat. Record

Record type

  • final ์ œํ•œ์ž ์ถ”๊ฐ€
  • ์ƒ์„ฑ์ž
  • equals
  • getter
  • hashcode, toString

N+1 ๋ฌธ์ œ๋ฅผ ์‹ค์ „์ ์œผ๋กœ ํ•ด๊ฒฐํ•ด ๋ณด์ž!

๊ฒฐ๋ก ๋ถ€ํ„ฐ

๋‚˜์˜ ๊ฒฝ์šฐ default batch size๋ฅผ ๊ฑธ๊ณ  ์ผ๋‹จ ๋„˜์–ด๊ฐ„๋‹ค.
์ŠคํŠธ๋ ˆ์Šค ๋ฐ›์œผ๋ฉด์„œ ๋ชจ๋“ ๊ฑธ ํŠœ๋‹ํ•˜๊ณ  ์‹ถ์ง€ ์•Š๊ธฐ๋„ ํ•˜๊ณ .. ๊ทธ์ •๋„๋กœ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š๋Š”๋‹ค.
๋ณ‘๋ชฉ์ด ๋ฐœ์ƒํ•ด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ๊ทธ ๋ถ€๋ถ„๋งŒ ํŠœ๋‹ํ•˜๋ฉด ๊ทธ๋งŒ!

default batch size๋Š” ํ•œ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋Š” ๋ ˆ์ฝ”๋“œ ์ˆ˜๋ฅผ ๋œปํ•˜๊ณ 
fetch join๋งŒํผ ๊ฐ•๋ ฅํ•˜๊ฒŒ ํ•œ๋ฐฉ ์ฟผ๋ฆฌ๋Š” ์•„๋‹ˆ์ง€๋งŒ
N+1์„ 1+1๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋А๋‚Œ์ด๋‹ค.

๋‹ค์‹œ๋งํ•ด ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ x๊ฐ€ ์žˆ์œผ๋ฉด x+1
๋‚˜์˜ ๊ฒฝ์šฐ post์— ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ image, hashtag, reply
3+1 -> 4๋ฒˆ์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐˆ๊ฒƒ์ด๋‹ค.

์‹ค์ œ๋กœ ํ™•์ธํ•ด ๋ณด์ž

Image

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 - ๊ณตํ†ต ์‘๋‹ต ํฌ๋งท + ์˜ˆ์™ธ์ฒ˜๋ฆฌ

** 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๋กœ..

์‹คํ–‰๊ฒฐ๊ณผ
Image

Image Image

์šฐ๋ฆฌ๋Š” ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด์•ผ ํ• ๊นŒ?
ํŒ€์›๋“ค์„ ํ•œ๋ช… ํ•œ๋ช… ์ฐพ์•„๊ฐ€ ์ˆ˜์ •์„ ๋ถ€ํƒํ•ด์•ผ ํ• ๊นŒ?
์‹œ๊ฐ„๋„ ๋งŽ์ด ๋“ค๊ณ  ํ•œ๋ช… ํ•œ๋ช… ๋งํ•˜๋ฉฐ ๊ฐˆ๋“ฑ์ด ์ƒ๊ธธ ๊ฐ€๋Šฅ์„ฑ์€?

๊ทธ๋ฆฌ๊ณ  ๋ฐฉ์‹ฌํ•œ ์‚ฌ์ด์— ์ฝ”๋“œ๊ฐ€ 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;
    }

}

์‘๋‹ต ์ข…๋ฅ˜์™€ ์ƒ๊ด€ ์—†์ด ์‘๋‹ต์„ ๊ฐ€๋กœ์ฑ„
๊ณตํ†ต ์‘๋‹ต ํฌ๋งท์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์ฝ”๋“œ์ด๋‹ค.

์ ์šฉ ํ›„ ์‹คํ–‰๊ฒฐ๊ณผ

Image Image Image

์„ธ๊ฐ€์ง€ ๋ฐฉ์‹ ๋ชจ๋‘ ํ†ต์ผ๋œ ์‘๋‹ต ํฌ๋งท์œผ๋กœ ๋‚˜๊ฐ„๋‹ค.

์ด๋ฅผ ์ ์šฉํ•œ ์ง€์‹์ธ ํ”„๋กœ์ ํŠธ ์‘๋‹ต์€ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋‹ค

{
  "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
    }
}
Image Image

๊ณง ์žˆ์„ ์•„์ด๋””์—์ด์…˜ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐœ๋ฐœํ• ๋•Œ
ํ”„๋ก ํŠธ์—”๋“œ์—๊ฒŒ ์ด๋ ‡๊ฒŒ ์‘๋‹ต์„ ๊น”๋”ํ•˜๊ฒŒ ์ฃผ๋ฉด ์—„์ฒญ๋‚˜๊ฒŒ ์‚ฌ๋ž‘ ๋ฐ›์„ ๊ฒƒ ๊ฐ™์ง€ ์•Š์€๊ฐ€?!?
๊ฑฐ๊ธฐ๋‹ค ์‹ค์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์Šคํ…œ์œผ๋กœ ๋ฐฑ์—”๋“œ ํŒ€์›์˜ ์‚ฌ๋ž‘์€ ๋ค

์šฐ๋ฆฌ ๋ชจ๋‘ ๋‹ค๋ฅธ ํŒ€์›์—๊ฒŒ ์‚ฌ๋ž‘๋ฐ›๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์ž!

๊ฒฐ๊ณผ

Create

Image Image

ํ˜„์žฌ ์—…๋กœ๋“œํ•œ ์ด๋ฏธ์ง€๋Š” ๋กœ์ปฌ์— ์ €์žฅ, ๋ฒ ํฌํ• ๋•Œ ๊ฒฝ๋กœ๋ฅผ ๋ฒ„ํ‚ท์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ๋จ

Read

Image Image

Soft Delete

Image Image

์‹œ๊ฐ„ ๋ถ€์กฑ์ด๋ผ๋Š” ํ•‘๊ณ„๋กœ ์ธํ•ด ๋กœ๊น…+ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์€ ๋ชปํ–ˆ๋‹ค
๋‹ค์Œ์— ๊ผญ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹คใ…œใ…œ

์ธ์ฆ ๋ฐฉ์‹

์„ธ์…˜ ์ธ์ฆ ๋ฐฉ์‹

๋กœ๊ทธ์ธ์„ ํ•˜๋ฉด ์„ธ์…˜ ์ €์žฅ์†Œ์— ์‚ฌ์šฉ์ž ์„ธ์…˜์„ ์ €์žฅํ•˜๊ณ 
์„ธ์…˜ ID๋ฅผ ์ฟ ํ‚ค๋กœ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „์†กํ•˜๋Š” ๋ฐฉ์‹.

์ฟ ํ‚ค๋Š” ๋ธŒ๋ผ์šฐ์ €์ƒ์—์„œ ์ž‘๋™ํ•˜๊ณ  ์ž๋™์ ์œผ๋กœ ์š”์ฒญ์— ํฌํ•จ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์—„์ฒญ ๊ฐ„ํŽธํ•˜๋‹ค!!
Spring Security ๊ธฐ๋ณธ ๊ตฌํ˜„ ๋ฐฉ์‹์ด ์„ธ์…˜ ์ธ์ฆ ๋ฐฉ์‹์ด๋‹ค.

ํ•˜์ง€๋งŒ ์„ธ์…˜ ์ €์žฅ์†Œ์— ์„ธ์…˜์„ ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€ํ•˜๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๊ณ 

์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๋Š” ํ™˜๊ฒฝ์ด๋ผ๋ฉด?
์ฆ‰ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด?

์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
ํŠนํžˆ!! ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ๋ชจ๋“  ์„œ๋ฒ„์— ๋˜‘๊ฐ™์€ ์„ธ์…˜์„ ์ €์žฅํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์˜คํžˆ๋ ค ๊ด€๋ฆฌ๊ฐ€ ํž˜๋“ค๋‹ค
์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‚˜์˜จ ๊ฒƒ์ด JWT์ด๋‹ค.

JWT

JWT๊ฐ€ ๋ฌด์—‡์ธ์ง€๋Š” ์ธํ„ฐ๋„ท์— ๋„ˆ๋ฌด๋„ˆ๋ฌด ๋งŽ์œผ๋‹ˆ ๋„˜์–ด๊ฐ€๊ณ 
์ฒ˜์Œ ์ ‘ํ–ˆ์„๋•Œ refresh token, black list, rotate refresh, refresh token ์„œ๋ฒ„ ์ €์žฅ, refresh token ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „์†ก ๋“ฑ๋“ฑ..
๋„ˆ๋ฌด ๋งŽ์€ ๋‚ด์šฉ๊ณผ ๋‹ค์–‘ํ•œ ๊ตฌํ˜„ ๋ฐฉ์‹ ๋•Œ๋ฌธ์— ํ˜ผ๋ž€์Šค๋Ÿฌ์› ๋Š”๋ฐ
๊ฒช์—ˆ๋˜ ๊ณผ์ • ์†์—์„œ ๊ณ ๋ฏผํ–ˆ๋˜ ๋‚ด์šฉ๋“ค๊ณผ ๊ฐœ์ธ์ ์ธ ์ƒ๊ฐ์„ ์ ์–ด ๋ณด๊ฒ ๋‹ค.

JWT - Access Token

JWT ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•˜๊ณ  ๋‚˜๋ฉด ์ด ํ† ํฐ์ด ์šฐ๋ฆฌ๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ๊ฒƒ์ด ๋งž๋Š”์ง€ ์ˆ˜ํ•™์ ์œผ๋กœ ๊ฒ€์ฆํ•˜๊ณ , ๋งŒ๋ฃŒ๋˜์—ˆ๋Š”์ง€๋งŒ ๊ฒ€์‚ฌํ•œ๋‹ค.
์ข€ ์–ด๋ ต๊ฒŒ ๋งํ•˜๋ฉด Verify Signature ๋ถ€๋ถ„์„ ์šฐ๋ฆฌ์˜ Secret key๋กœ ๋ณตํ˜ธํ™” ํ•ด ์กฐ์ž‘๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
์ด๊ฒŒ ์ฝ”๋“œ์ƒ์—์„œ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•œ Signature Exception์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๋ถ€๋ถ„์ด๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ๋ˆ„๊ตฌ์—๊ฒŒ ๋ฐœ๊ธ‰ํ–ˆ๋Š”์ง€ ๊ธฐ์–ตํ•˜์ง€๋„ ์•Š๊ณ , ๊ฒ€์ฆํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ.

๊ทธ๋Ÿผ ๋ˆ„๊ตฌ์—๊ฒŒ ๋ฐœ๊ธ‰ํ•œ ํ† ํฐ์ธ์ง€ ํ™•์ธํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ† ํฐ์ด ํ„ธ๋ฆฐ๋‹ค๋ฉด ํ•ด๋‹น access token์ด ์œ ํšจํ•œ ์‹œ๊ฐ„๋™์•ˆ ๊ณ„์†ํ•ด์„œ ๊ณต๊ฒฉ์„ ๋ฐ›์„ ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.
์ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ access token์˜ ์œ ํšจ ์‹œ๊ฐ„์„ ์งง๊ฒŒ ์„ค์ • ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๊ณ ์•ˆ๋œ ๊ฒƒ์ด๋‹ค. (์˜ˆ๋ฅผ๋“ค์–ด 30๋ถ„)
ํ•˜์ง€๋งŒ ์‚ฌ์šฉ์ž๋Š” ์ด ๊ฒฝ์šฐ์— 30๋ถ„๋งˆ๋‹ค ๋‹ค์‹œ ๋กœ๊ทธ์ธ์„ ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถˆํŽธํ•  ๊ฒƒ์ด๋‹ค.
๊ทธ๋ž˜์„œ ๋‚˜์˜จ ๊ฒƒ์ด Refresh Token.

JWT - 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์ด๋‹ค.

JWT - Refresh Token: Rotate And BlackList

์ด ๋ฐฉ๋ฒ•์€ 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์„ ์„œ๋ฒ„์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๋‚˜์™”๋‹ค.

JWT - 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์™€ ๋กœ์ง์ด ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถ„๋ฆฌํ•˜๊ณ  ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด ์žฌ์‚ฌ์šฉํ–ˆ๋‹ค.

ํ…Œ์ŠคํŠธ

ํ† ํฐ์ด ์—†์„๋•Œ

ํ† ํฐ ์žˆ์„๋•Œ

Postman ์ž๋™ํ™”

์ฒ˜์Œ์— ๋กœ๊ทธ์ธ ํ›„ ๋ฐ›์€ access token์„ ๋ณต๋ถ™์„ ์ฐธ ๋งŽ์ด ํ–ˆ๋Š”๋ฐ
์ด๋ฒˆ์— ์ž๋™ํ™” ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค!.!

Environments์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋งŒ๋“ค์–ด ์ฃผ๊ณ 

์ปฌ๋ ‰์…˜์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ ํƒ

ํ† ํฐ์„ ๋ฐ›์•„์˜ค๋Š” ์—”๋“œํฌ์ธํŠธ (๋กœ๊ทธ์ธ)์— script๋ฅผ ์ ์–ด์ค€๋‹ค

var token = pm.response.headers.get("access");
pm.environment.set("access", token);

๋งˆ์ง€๋ง‰์œผ๋กœ access token์ด ํ•„์š”ํ•œ ์—”๋“œํฌ์ธํŠธ์— ํ•˜๋“œ์ฝ”๋”ฉํ•˜์ง€ ๋ง๊ณ  {{access}}๋กœ ์ ์–ด์ค€๋‹ค
์•„์ฃผ ํŽธํ•˜๋‹ค

docker ์ ์šฉ

ํŠน๋ณ„ํ•œ ๊ฑด ์—†์—ˆ๋‹ค!
docker compose๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ ์ปจํ…Œ์ด๋„ˆ๋ผ๋ฆฌ ํฌํŠธ ์ง€์ •ํ•ด์ฃผ๋Š” ๋ถ€๋ถ„์ด ํ—ท๊ฐˆ๋ ธ์–ด์„œ ์ •๋ฆฌํ•ด๋ดค๋‹ค.

๋กœ์ปฌ์—์„œ db๊ฐ€ 5432 ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋‹ˆ
๊ฐ™์€ db ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋„์šฐ๊ธฐ ์œ„ํ•ด์„œ๋Š” 5431 ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ–ˆ๊ณ 
Redis๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ 6379๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋‹ˆ 6378๋กœ ํฌํŠธ ํฌ์›Œ๋”ฉ์„ ํ•ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด 8080:8080์œผ๋กœ ํฌ์›Œ๋”ฉํ•œ ๋ฐฑ์—”๋“œ ์ปจํ…Œ์ด๋„ˆ๋Š” ๋„์ปค db, ๋„์ปค redis๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์–ด๋–ค ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?
๊ทธ๋ฆผ์ฒ˜๋Ÿผ 5432, 6379์ด๋‹ค.
์‚ฌ์‹ค ์ƒ๊ฐํ•ด๋ณด๋ฉด ๊ฐ„๋‹จํ•œ๋ฐ ๋„์ปค ๋„คํŠธ์›Œํฌ ์•ˆ์—์„œ ํ†ต์‹ ํ•˜๋Š” ๊ฑฐ๋‹ˆ๊นŒ..

docker-compose.prod

application-prod ์ฒ˜๋Ÿผ profile์„ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ํ•œ๋‹ค.
์ด์™€ ๊ด€๋ จํ•ด์„œ
\1. DB ์ปจํ…Œ์ด๋„ˆ ์‚ญ์ œ 2. nginx ๊ตฌํ˜„ 3. Docker hub
3๊ฐ€์ง€๊ฐ€ ์ นํ˜€์žˆ๋Š”๋ฐ ์ด๊ฒŒ ๋ฌด์Šจ ๋ง์ธ์ง€ ์„คํŽด๋ณด๋ฉด์„œ ๋‚ด ์ƒ๊ฐ์„ ์ •๋ฆฌํ•ด๋ณด๊ฒ ๋‹ค

Docker ์‹ค์ œ ํ™œ์šฉ

  1. DB ์ปจํ…Œ์ด๋„ˆ ์‚ญ์ œ
    AWS๋กœ ์ƒ๊ฐํ•ด๋ณด๋ฉด ํ•˜๋‚˜์˜ ec2 ์ธ์Šคํ„ด์Šค ์•ˆ์—์„œ ์ปจํ…Œ์ด๋„ˆ 3๊ฐœ(spring, db, redis) ๋ชจ๋‘๊ฐ€ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋Š” ์ƒํ™ฉ.
    CPU, Memory ์‚ฌ์šฉ๋Ÿ‰์ด ๋‚จ์•„๋‚˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค.
    ์ฒซ ํ”„๋กœ์ ํŠธ๋ฅผ ์ด๋ ‡๊ฒŒ ํ–ˆ๋‹ค๊ฐ€ ์„œ๋ฒ„๊ฐ€ ๊ณ„์† ์ฃฝ์—ˆ๋˜ ๊ธฐ์–ต์ด ๋‚œ๋‹ค.
  2. Nginx ๊ตฌํ˜„
    ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์„ ์œ„ํ•ด ์‚ฌ์šฉ.
    ELB๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, ์ž‘๋™ํ•˜๋Š” ๊ณ„์ธต์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋‘˜๋‹ค ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ์ข‹๊ณ  ์ผ๋ฐ˜์ ์ด๋ผ๋Š” ๊ธ€์„ ๋ด์„œ ๋’ค์—์„œ ์ƒ๊ฐ๊ณผ ๊ฒ€์ฆ์„ ํ•ด๋ณด๊ฒ ๋‹ค.
  3. 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, Web Application Server

์™„์ „ ๋„์ปค์— ๊ด€๋ จ๋œ ๋‚ด์šฉ์€ ์•„๋‹ˆ์ง€๋งŒ ์—”์ง„์—‘์Šค๋ฅผ ๋ณด๋‹ค๊ฐ€ ๋ฌธ๋“ ์ƒ๊ฐ์ด ๋‚˜์„œ ์ •๋ฆฌํ•ด๋ดค์Šต๋‹ˆ๋‹ค

๊ตฌ๊ธ€์— ๊ฒ€์ƒ‰ํ•˜๋ฉด ์ด๋Ÿฐ ๊ทธ๋ฆผ์ด ๋‚˜์˜ค๋Š”๋ฐ,
Web Server๋Š” ์ •์  ์ž์›์„ ์ œ๊ณตํ•˜๊ณ , WAS๋Š” ๋™์  ์ž์›์„ ์ œ๊ณตํ•œ๋‹ค๊ณ  ๋‚˜์™€ ์žˆ๋‹ค.
์ •์  ์ž์›์€ HTML, CSS, JS, ์ด๋ฏธ์ง€ ๋“ฑ๋“ฑ
๊ทธ๋ž˜์„œ Webserver์˜ ์˜ˆ์‹œ๋กœ Nginx, Apache๊ฐ€ ๋‚˜์™€์žˆ๊ณ 
WAS์˜ ์˜ˆ์‹œ๋กœ Tomcat, Jetty ๋“ฑ์ด ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.

์•ฝ๊ฐ„ ์™€๋‹ฟ์ง€ ์•Š๋Š”๋ฐ, ๊ณ„์‚ฐ์ด ๋“ค์–ด๊ฐ€๋Š” ๋“ฑ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋†ˆ์€ WAS์ด๊ณ 
WAS๋Š” WebServer๊ฐ€ ํ•˜๋Š” ์ผ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ
๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” WAS ์ž์›์ด ์•„๊นŒ์šฐ๋‹ˆ ์„œ๋น™๋งŒ ํ•˜๋ฉด ๋˜๋Š” ์ผ์€ WebServer์—๊ฒŒ ์‹œํ‚ค์ž๋Š” ๊ฒƒ์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

Tomcat, Spring, SpringBoot

์ฒ˜์Œ ์ด๋ฅผ ๋ณด๊ณ  ๋“  ์˜๋ฌธ์€ ์šฐ๋ฆฌ๊ฐ€ ๊ฐœ๋ฐœํ•˜๋Š”๊ฑด 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๋กœ ์Šคํ”„๋ง ๋นˆ์„ ๋“ฑ๋กํ•ด ๋†“์œผ๋ฉด
๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์ด ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

์ด ๊ทธ๋ฆผ์ด ๊ฐ€์žฅ ์ดํ•ด๊ฐ€ ์ž˜ ๊ฐ€๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

DispatcherServlet

์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฐพ์•„์„œ ์œ„์ž„(ํ˜ธ์ถœ) ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๋ญ˜๊นŒ?
๊ณผ์ •์€ ์ด๋ ‡๋‹ค๊ณ  ํ•œ๋‹ค.

  1. ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์ด ๋ฐ›์Œ
  2. ์œ„์ž„ํ•  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฐพ์Œ
  3. ์š”์ฒญ์„ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์œ„์ž„ํ•  ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ฐพ์•„ ์ „๋‹ฌ
  4. ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์š”์ฒญ์„ ์œ„์ž„
  5. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ
  6. ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ฐ˜ํ™˜๊ฐ’์„ ๋ฐ˜ํ™˜
  7. ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜๊ฐ’ ์ฒ˜๋ฆฌ
  8. ์„œ๋ฒ„ ์‘๋‹ต์„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ˜ํ™˜

๊ฐ™์€ ์ˆœ์„œ๋กœ ์ข€๋” ์ž์„ธํ•˜๊ฒŒ ๋ณด์ž (๋ณด์‹ค ๋ถ„๋“ค์„ ์œ„ํ•ด : tomcat-embed-core ํŒจํ‚ค์ง€์— ์žˆ์Šต๋‹ˆ๋‹ค)

  1. ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ๋””์ŠคํŒจ์ฒ˜ ์„œ๋ธ”๋ฆฟ์ด ๋ฐ›์Œ
    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. ์š”์ฒญ์„ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์œ„์ž„ํ•  ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ฐพ์•„ ์ „๋‹ฌ
    2, 3 -1. doDispatch ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ฐพ์Œ
    (์ด ๋ถ€๋ถ„ ๋‚ด๋ถ€๋Š” ์ƒ๋žต ํ•˜๊ฒ ๋‹ค..)

  4. ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์ปจํŠธ๋กค๋Ÿฌ๋กœ ์š”์ฒญ์„ ์œ„์ž„
    4-1. ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๋ฅผ ํ†ตํ•ด ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ 4-2. ๊ตฌํ˜„์ฒด์ธ AbstractHandlerMethodAdapter์˜ handle ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€๋ฅผ ๋ณด๋ฉด
    ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ModelAndView์ธ๋ฐ MVC์˜ ๋ƒ„์ƒˆ๊ฐ€ ๋‚˜๋Š”๊ฒŒ ๋А๊ปด์ง€๋Š”๊ฐ€?

  5. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ฒ˜๋ฆฌ

  6. ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ฐ˜ํ™˜๊ฐ’์„ ๋ฐ˜ํ™˜

  7. ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜๊ฐ’ ์ฒ˜๋ฆฌ

  8. ์„œ๋ฒ„ ์‘๋‹ต์„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ˜ํ™˜

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

rds

ec2 docker ps

ec2 docker be container ๋‚ด๋ถ€ ๋กœ๊ทธ

ํšŒ์›๊ฐ€์ž…

๋กœ๊ทธ์ธ

๊ธ€ ์ž‘์„ฑ

๊ธ€ ์ฐพ๊ธฐ

docker hub

๋‹ค์ด์–ด๊ทธ๋žจ

๊ฐ„๋‹จํ•œ ํ”„๋กœ์ ํŠธ์—ฌ์„œ ์ด๋ ‡๊ฒŒ ํ–ˆ์ง€๋งŒ ์ข‹์€ ๊ตฌ์กฐ๋Š” ์•„๋‹ˆ๋‹ค

๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ ์•„ํ‚คํ…์ณ๋Š” ์ด๋ ‡๊ฒŒ ์„ค๊ณ„ํ–ˆ๋‹ค

๊ฐœ์„ ์ ์€ ec2๋„ ํ”„๋ผ์ด๋น— ์„œ๋ธŒ๋„ท์— ์žˆ๋Š”๊ฒŒ ์ข‹๊ณ  ๊ทธ๋Ÿฌ๋ ค๋ฉด nat gateway๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

์ด๋Ÿฐ๋А๋‚Œ

์ด๋ฒˆ์— ์ƒˆ๋กœ ํ•ด๋ด์„œ ์™„์ „ํžˆ ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ์ง€๋งŒ
๊ฐ€์šฉ์˜์—ญ ๋‘๊ฐœ์— ๊ฑธ์ณ ์žˆ๋Š” ์ด์œ ๋Š” ํฌ๊ฒŒ ๋‘๊ฐ€์ง€ ์ด์œ ์ธ๋ฐ

  1. ์žฅ์•  ๋ฐœ์ƒ์‹œ ๊ฐ€์šฉ์˜์—ญ์„ ๋ฐ”๊ฟ”์„œ ์„œ๋น„์Šค๋ฅผ ๊ณ„์† ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด
  2. ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์„ ์œ„ํ•ด(๋ถ€ํ•˜ ๋ถ„์‚ฐ)

์ถ”๊ฐ€ ์„ค๋ช…์œผ๋กœ
rds, elastic cache ๋ถ€๋ถ„์— ์ธ์Šคํ„ด์Šค ์—ฌ๋Ÿฌ๊ฐœ ๋– ์žˆ๋Š”๊ฒŒ
๋งŽ์ด ๋ดค์„ ๋‹ค์ค‘AZ ์˜ต์…˜์ด๋‹ค

BootJar

bootjarํ–ˆ์„๋•Œ

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/

bootjar์‹œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ฃผ์ž…

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๋กœ ๋งจ๋‚  ํ–ˆ๋Š”๋ฐ ๋‹ค์Œ๋ฒˆ์— ์ ์šฉํ•ด๋ด์•ผ๊ฒ ๋‹ค

Nginx

Load Balancing

AWS ELB - ALB, NLB, CLB, GWLB

Nginx + ELB ???

About

ceos back-end 21st naver knowledge-in clone coding project

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages