Skip to content

CEOS-Developers/spring-instagram-20th

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

37 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

spring-instagram-20th

CEOS 20th BE study - instagram clone coding

์„ธ์˜ค์Šค_์ธ์Šคํƒ€๊ทธ๋žจ

์ธ์Šคํƒ€๊ทธ๋žจ ์„œ๋น„์Šค ์†Œ๊ฐœ

  • ์‚ฌ์šฉ์ž๊ฐ€ ๊ธ€๊ณผ ์‚ฌ์ง„์„ ์—…๋กœ๋“œํ•˜๊ณ , ๊ฒŒ์‹œ๋ฌผ์— ๋Œ€ํ•œ ๋Œ“๊ธ€๊ณผ ๋Œ€๋Œ“๊ธ€์„ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์ข‹์•„์š”๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋Š” SNS ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์œ ์ € ๊ฐ„์— 1: 1 ๋ฉ”์‹œ์ง€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ERD ์„ค๋ช…

๊ธฐ๋Šฅ๋ชฉ๋ก

  • ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ
  • ๊ฒŒ์‹œ๊ธ€์— ์‚ฌ์ง„๊ณผ ํ•จ๊ป˜ ๊ธ€ ์ž‘์„ฑํ•˜๊ธฐ
  • ๊ฒŒ์‹œ๊ธ€์— ๋Œ“๊ธ€ ๋ฐ ๋Œ€๋Œ“๊ธ€ ๊ธฐ๋Šฅ
  • ๊ฒŒ์‹œ๊ธ€์— ์ข‹์•„์š” ๊ธฐ๋Šฅ
  • ๊ฒŒ์‹œ๊ธ€, ๋Œ“๊ธ€, ์ข‹์•„์š” ์‚ญ์ œ ๊ธฐ๋Šฅ
  • ์œ ์ € ๊ฐ„ 1:1 DM ๊ธฐ๋Šฅ

์ถ”๊ฐ€ ๊ณผ์ œ: Spring Data JPA์—์„œ EntityManager๋ฅผ ์–ด๋–ป๊ฒŒ ์ฃผ์ž…ํ•˜๋Š”๊ฐ€?

1. EntityManager ๋ž€?

  • JPA๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด database ๊ตฌ์กฐ์™€ ๋งคํ•‘๋œ JPA Entity๋ฅผ ๋จผ์ € ์ƒ์„ฑํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ๋ชจ๋“  JPA์˜ ๋™์ž‘์€ ์ด Entity๋“ค์„ ๊ธฐ์ค€์œผ๋กœ ๋Œ์•„๊ฐ€๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ๋•Œ Entity๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ EntityManager์ด๋‹ค.
  • ๋น„์ƒํƒœ์„ฑ(stateless) ์ปดํฌ๋„ŒํŠธ๋กœ, ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์—ฐ๊ฒฐ.
  • ๊ฐ ์š”์ฒญ์— ๋”ฐ๋ผ ์ƒˆ๋กœ์šด EntityManager๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด ์ƒ์„ฑ๋œ EntityManager๋Š” ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋  ๋•Œ ์ž๋™์œผ๋กœ ์ฃผ์ž…๋˜๊ณ , ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜๋ฉด ์ž๋™์œผ๋กœ ์ข…๋ฃŒ

2. SimpleJpaRepository์—์„œ EntityManager ์ฃผ์ž…

  • SimpleJpaRepository๋Š” ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š” Spring Bean์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉ๋˜๋Š” EntityManager๋Š” ํŠธ๋žœ์žญ์…˜์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋‹จ์ผ EntityManager ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณ ์ •์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ๋œ๋‹ค.
  • ์ฆ‰, Spring์€ EntityManager๋ฅผ ์ง์ ‘ ์ฃผ์ž…ํ•˜์ง€ ์•Š๊ณ , EntityManager ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ฃผ์ž….
  • ์ด ํ”„๋ก์‹œ๋Š” ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋  ๋•Œ๋งˆ๋‹ค ์˜ฌ๋ฐ”๋ฅธ EntityManager๋ฅผ ์—ฐ๊ฒฐ

3. ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•œ ๋™์ž‘ ๋ฐฉ์‹

  • EntityManager ํ”„๋ก์‹œ๋Š” ์‹ค์ œ EntityManager๋ฅผ ์š”์ฒญํ•  ๋•Œ ๋™์  ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜ ์ปจํ…์ŠคํŠธ์— ๋งž๋Š” ์‹ค์ œ EntityManager๋ฅผ ์—ฐ๊ฒฐ.
  • SimpleJpaRepository ๊ฐ™์€ ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด์—์„œ EntityManager๋ฅผ ์ƒ์„ฑ์ž ์ฃผ์ž…์œผ๋กœ ๋ฐ›๋”๋ผ๋„, ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค๋กœ ํŠธ๋žœ์žญ์…˜ ๋ณ„๋กœ ์‹ค์ œ ๋‹ค๋ฅธ EntityManager๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ tread-safe ํ•˜๋‹ค.
  • @PersistContext ๋ฅผ ์ด์šฉํ•˜์—ฌ EntityManager ๋ฅผ ์ฃผ์ž…๋ฐ›๊ฒŒ ๋˜๋ฉด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ EntityManger ๊ฐ€ 1๊ฐœ์˜ ์Šค๋ ˆ๋“œ์— ํ• ๋‹น๋˜๋„๋ก ์ œํ•œํ•ด์ค€๋‹ค.

์ถ”๊ฐ€ ๊ณผ์ œ: Spring Data JPA์—์„œ EntityManager๋ฅผ ์–ด๋–ป๊ฒŒ ์ฃผ์ž…ํ•˜๋Š”๊ฐ€?

1. EntityManager ๋ž€?

  • JPA๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด database ๊ตฌ์กฐ์™€ ๋งคํ•‘๋œ JPA Entity๋ฅผ ๋จผ์ € ์ƒ์„ฑํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ๋ชจ๋“  JPA์˜ ๋™์ž‘์€ ์ด Entity๋“ค์„ ๊ธฐ์ค€์œผ๋กœ ๋Œ์•„๊ฐ€๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ๋•Œ Entity๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ EntityManager์ด๋‹ค.
  • ๋น„์ƒํƒœ์„ฑ(stateless) ์ปดํฌ๋„ŒํŠธ๋กœ, ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์—ฐ๊ฒฐ.
  • ๊ฐ ์š”์ฒญ์— ๋”ฐ๋ผ ์ƒˆ๋กœ์šด EntityManager๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด ์ƒ์„ฑ๋œ EntityManager๋Š” ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋  ๋•Œ ์ž๋™์œผ๋กœ ์ฃผ์ž…๋˜๊ณ , ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜๋ฉด ์ž๋™์œผ๋กœ ์ข…๋ฃŒ

2. SimpleJpaRepository์—์„œ EntityManager ์ฃผ์ž…

  • SimpleJpaRepository๋Š” ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ๋˜๋Š” Spring Bean์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉ๋˜๋Š” EntityManager๋Š” ํŠธ๋žœ์žญ์…˜์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋‹จ์ผ EntityManager ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณ ์ •์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ๋œ๋‹ค.
  • ์ฆ‰, Spring์€ EntityManager๋ฅผ ์ง์ ‘ ์ฃผ์ž…ํ•˜์ง€ ์•Š๊ณ , EntityManager ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ฃผ์ž….
  • ์ด ํ”„๋ก์‹œ๋Š” ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋  ๋•Œ๋งˆ๋‹ค ์˜ฌ๋ฐ”๋ฅธ EntityManager๋ฅผ ์—ฐ๊ฒฐ

3. ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•œ ๋™์ž‘ ๋ฐฉ์‹

  • EntityManager ํ”„๋ก์‹œ๋Š” ์‹ค์ œ EntityManager๋ฅผ ์š”์ฒญํ•  ๋•Œ ๋™์  ๋ฐ”์ธ๋”ฉ์„ ํ†ตํ•ด ํŠธ๋žœ์žญ์…˜ ์ปจํ…์ŠคํŠธ์— ๋งž๋Š” ์‹ค์ œ EntityManager๋ฅผ ์—ฐ๊ฒฐ.
  • SimpleJpaRepository ๊ฐ™์€ ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด์—์„œ EntityManager๋ฅผ ์ƒ์„ฑ์ž ์ฃผ์ž…์œผ๋กœ ๋ฐ›๋”๋ผ๋„, ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค๋กœ ํŠธ๋žœ์žญ์…˜ ๋ณ„๋กœ ์‹ค์ œ ๋‹ค๋ฅธ EntityManager๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ tread-safe ํ•˜๋‹ค.
  • @PersistContext ๋ฅผ ์ด์šฉํ•˜์—ฌ EntityManager ๋ฅผ ์ฃผ์ž…๋ฐ›๊ฒŒ ๋˜๋ฉด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ EntityManger ๊ฐ€ 1๊ฐœ์˜ ์Šค๋ ˆ๋“œ์— ํ• ๋‹น๋˜๋„๋ก ์ œํ•œํ•ด์ค€๋‹ค.

Spring JPA ์‹ฌํ™” ๊ณผ์ œ

์ง€๋‚œ ์ฃผ ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง

  • ๋Œ€๋Œ“๊ธ€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ณ„๋„๋กœ ๊ตฌํ˜„ํ–ˆ๋Š”๋ฐ, ๊ทธ๊ฒƒ์„ ์‚ญ์ œํ•˜๊ณ  Comment ์—”ํ‹ฐํ‹ฐ ๋‚ด๋ถ€์— parentComment ๋ถ€๋ชจ๋Œ“๊ธ€ ํ•„๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.
@JoinColumn(name = "post_id")
    private Comment parentComment; // ์ž๊ธฐ์ฐธ์กฐ

N+1 ๋ฌธ์ œ

Post(many) ์ชฝ ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๋Š” ์ƒํ™ฉ.

  • N์€ Post์˜ ์ˆ˜, 1์€ Post๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฉ”์ธ ์ฟผ๋ฆฌ
  • 1๊ฐœ์˜ Post ์กฐํšŒ ์ฟผ๋ฆฌ์™€ ๊ฐ Post์— ์—ฐ๊ฒฐ๋œ User๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” 2๊ฐœ์˜ ์ถ”๊ฐ€ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒ
  • Post์˜ ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต์ ์œผ๋กœ User ์กฐํšŒ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด N+1 ๋ฌธ์ œ. User๋Š” ํ•œ ๋ฒˆ๋งŒ ๊ฐ€์ ธ์™€๋„ ๋˜์ง€๋งŒ, Post ์ˆ˜๋งŒํผ ๋ถˆํ•„์š”ํ•œ ์ถ”๊ฐ€ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰
  • ์ฆ‰, Post๊ฐ€ 2๊ฐœ๋ผ์„œ User๋ฅผ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๋‘ ๋ฒˆ ๋ฐœ์ƒํ•œ ๊ฒƒ, ๊ฐ Post์— ์—ฐ๊ฒฐ๋œ User๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ๋ณ„๋„์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒ

-> Post์™€ User๋ฅผ ํ•จ๊ป˜ ํ•œ ๋ฒˆ์˜ ์ฟผ๋ฆฌ๋กœ ๊ฐ€์ ธ์˜ค๋„๋ก ํŽ˜์น˜ ์ „๋žต์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š์œผ๋ฉด, Post๊ฐ€ ๋” ๋งŽ์•„์งˆ์ˆ˜๋ก User๋ฅผ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ๋„ ๋น„๋ก€ํ•ด์„œ ๊ณ„์† ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋œ๋‹ค.

ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ ๊ฒฝ์šฐ์—๋Š”, getUser() ์„ ์‹คํ–‰ํ•  ๋•Œ ์‹ค์ œ DB ์—์„œ ๊ฐ€์ ธ์™€์„œ ํ•ด๋‹น ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ฟผ๋ฆฌ๋ฅผ ๋‚˜์ค‘์— ๊ฐ€์ ธ์™€์„œ ์‹คํ–‰ํ•˜๋ฏ€๋กœ ์ถ”๊ฐ€์ ์ธ ์ฟผ๋ฆฌ ๋ฐœ์ƒ.

Hibernate: 
    insert 
    into
        user
        (email, password) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        user
        (email, password) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        post
        (caption, created_at, image_url, user_id) 
    values
        (?, ?, ?, ?)
Hibernate: 
    insert 
    into
        post
        (caption, created_at, image_url, user_id) 
    values
        (?, ?, ?, ?)
Hibernate: 
    select
        p1_0.post_id,
        p1_0.caption,
        p1_0.created_at,
        p1_0.image_url,
        p1_0.user_id 
    from
        post p1_0
post = com.ceos20.instagram.Domain.Post@6da53709
->post.getUser().getClass() = class com.ceos20.instagram.Domain.User$HibernateProxy$SWmpqPqD
Hibernate: 
    select
        u1_0.user_id,
        u1_0.email,
        u1_0.password,
        p1_0.user_id,
        p1_0.post_id,
        p1_0.caption,
        p1_0.created_at,
        p1_0.image_url 
    from
        user u1_0 
    left join
        post p1_0 
            on u1_0.user_id=p1_0.user_id 
    where
        u1_0.user_id=?
->post.getUser() = com.ceos20.instagram.Domain.User@3f1eb1bc
post = com.ceos20.instagram.Domain.Post@2b55ac77
->post.getUser().getClass() = class com.ceos20.instagram.Domain.User$HibernateProxy$SWmpqPqD
Hibernate: 
    select
        u1_0.user_id,
        u1_0.email,
        u1_0.password,
        p1_0.user_id,
        p1_0.post_id,
        p1_0.caption,
        p1_0.created_at,
        p1_0.image_url 
    from
        user u1_0 
    left join
        post p1_0 
            on u1_0.user_id=p1_0.user_id 
    where
        u1_0.user_id=?
->post.getUser() = com.ceos20.instagram.Domain.User@4ff1b0d

join fetch ์‚ฌ์šฉํ•˜์—ฌ ์ง€์—ฐ๋กœ๋”ฉ ๋ฌธ์ œ ํ•ด๊ฒฐ

@Transactional
    public List<Post> findAll() {
        return em.createQuery("SELECT p FROM Post p JOIN FETCH p.user", Post.class)
                .getResultList(); // User๋ฅผ ํ•จ๊ป˜ ์ฆ‰์‹œ ๋กœ๋”ฉํ•˜์—ฌ ๋ฐ˜ํ™˜
    }
  • post.getUser()๊ฐ€ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ์‹ค์ œ User ์—”ํ‹ฐํ‹ฐ๋กœ ๋กœ๋“œ
  • post์™€ ๊ด€๋ จ๋œ user ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋ฒˆ์— ์กฐํšŒํ•˜์—ฌ n+1 ๋ฌธ์ œ ํ•ด๊ฒฐ.
  • ๋งŒ์•ฝ JPA ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด, @EntityGraph ์‚ฌ์šฉ ๊ฐ€๋Šฅ.
Hibernate: 
    select
        p1_0.post_id,
        p1_0.caption,
        p1_0.created_at,
        p1_0.image_url,
        u1_0.user_id,
        u1_0.email,
        u1_0.password,
        u1_0.username 
    from
        post p1_0 
    join
        user u1_0 
            on u1_0.user_id=p1_0.user_id
Hibernate: 
    select
        p1_0.user_id,
        p1_0.post_id,
        p1_0.caption,
        p1_0.created_at,
        p1_0.image_url 
    from
        post p1_0 
    where
        p1_0.user_id=?
Hibernate: 
    select
        p1_0.user_id,
        p1_0.post_id,
        p1_0.caption,
        p1_0.created_at,
        p1_0.image_url 
    from
        post p1_0 
    where
        p1_0.user_id=?

DTO ๊ตฌํ˜„

  • DTO(Data Transfer Object)๋ž€: ๊ณ„์ธต๊ฐ„ ๋ฐ์ดํ„ฐ ๊ตํ™˜์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด

์‚ฌ์šฉ ์ด์œ 

  • Entity ์— ๊ด€ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์™ธ๋ถ€์— ๋…ธ์ถœ์‹œํ‚ค์ง€ ์•Š๊ณ , ์ง์ ‘์ ์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ.
  • Entity ํด๋ž˜์Šค์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์„ ํƒ์ ์œผ๋กœ DTO์— ๋‹ด์•„์„œ ์ƒ์„ฑํ•ด ์‚ฌ์šฉ

DTO ์‚ฌ์šฉ ์˜ˆ์‹œ

@Data
public class CommentRequest {
    private final String comment;

    @Builder
    public CommentRequest(String comment) {
        this.comment = comment;
    }

    public Comment toEntity(User writer, Post post, Comment parentComment) {
        return Comment.builder()
                .comment(comment) // DTO์˜ ๋Œ“๊ธ€ ๋‚ด์šฉ์„ ์—”ํ‹ฐํ‹ฐ์— ์„ค์ •
                .user(writer)
                .parentComment(parentComment)
                .post(post)
                .build();
    }

}
  • toEntity ๋ฉ”์„œ๋“œ๋Š” DTO ๊ฐ์ฒด์— ๋‹ด๊ธด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ Comment ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑ
  • User, Post, Comment ์—”ํ‹ฐํ‹ฐ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„, ์ด ๊ฐ’๋“ค์„ ์ƒˆ๋กœ์šด Comment ์—”ํ‹ฐํ‹ฐ์— ์„ค์ •
  • DTO ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ฑฐ๋‚˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์‚ฌ์šฉ

๋Œ“๊ธ€ ์—…๋ฐ์ดํŠธ์— ๋Œ€ํ•œ ์„œ๋น„์Šค ์ฝ”๋“œ

    @Transactional
    public void update(Long postId, Long userId, CommentRequest dto) {
        Comment comment = commentRepository.findByPostIdAndUserId(postId, userId).orElseThrow(() ->
                new IllegalArgumentException("ํ•ด๋‹น ๋Œ“๊ธ€์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. " + userId));

        comment.update(dto.getComment());
    }
  • dto.getComment()๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์ƒˆ๋กœ์šด ๋Œ“๊ธ€ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜จ๋‹ค.
  • Comment ์—”ํ‹ฐํ‹ฐ์— update ๋ฉ”์„œ๋“œ๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ์–ด, dto๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ๋Œ“๊ธ€ ๋‚ด์šฉ์„ ํ†ตํ•ด ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐฑ์‹ 
  • DTO ์•ˆ์— @Builder ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑ์ž์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ์ฃผ์ž…

์˜ˆ์™ธ ์ฒ˜๋ฆฌ

๋žŒ๋‹ค ํ‘œํ˜„์‹๊ณผ orElseThrow()

  • Optional์ด ๋น„์–ด์žˆ์„ ๊ฒฝ์šฐ, ์ฆ‰ ๊ฐ’์ด ์—†์„ ๋•Œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋„๋ก ํ•˜๋Š” ๋ฉ”์„œ๋“œ
  • ๊ฐ’์ด ํ•„์ˆ˜์ ์œผ๋กœ ์กด์žฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์ž์ฃผ ์‚ฌ์šฉ

๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ.

@Transactional
    public void sendMessage(Long roomId, ChatDto chatDto) {
        Chatroom chatroom = chatRoomRepository.findById(roomId)
                .orElseThrow(() -> new IllegalArgumentException("ํ•ด๋‹น ์ฑ„ํŒ…๋ฐฉ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. roomId=" + roomId));

        ChatMessage newMessage = chatDto.toEntity(chatroom);
        messageRepository.save(newMessage);
    }

Optional.isPresent()

  • ๊ฐ’์ด ์กด์žฌํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•œ ํ›„, ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š” ๋ฐฉ์‹
  • ์กด์žฌํ•  ๊ฒฝ์šฐ get()์œผ๋กœ ๊ฐ’์„ ๊บผ๋‚ธ๋‹ค. ์ฑ„ํŒ…๋ฐฉ ์ƒ์„ฑ ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ
@Transactional
    public Chatroom createChatroom(String roomName, User sender, User receiver) {

        // ๋™์ผํ•œ ์‚ฌ์šฉ์ž ๊ฐ„์˜ ์ฑ„ํŒ…๋ฐฉ์ด ์žˆ๋Š”์ง€ ํ™•์ธ
        Optional<Chatroom> existingChatroom = chatRoomRepository.findByUserIds(sender.getUserId(), receiver.getUserId());

        if (existingChatroom.isPresent()) {
            return existingChatroom.get(); // ์กด์žฌํ•˜๋Š” ์ฑ„ํŒ…๋ฐฉ์„ ๋ฐ˜ํ™˜
        }
  • ํ˜„์žฌ ์ฑ„ํŒ…๋ฐฉ์ด ์กด์žฌํ•˜๋Š” ์ง€ ํ™•์ธํ•œ ํ›„, ์ฐธ์ด๋ผ๋ฉด .get() ๋ฉ”์„œ๋“œ๋กœ ์กด์žฌํ•˜๋Š” ์ฑ„ํŒ…๋ฐฉ์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•œ๋‹ค.

๋‹จ์œ„ ํ…Œ์ŠคํŠธ

Mockito

Mock ์ด๋ž€?

  • ์ง„์งœ ๊ฐ์ฒด์™€ ๋น„์Šทํ•˜์ง€๋งŒ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๊ฐ™์ง€ ์•Š๊ณ  ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์ง์ ‘ ํ–‰๋™์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด

Stubbing

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ Mock ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, Mock์˜ ํŠน์ • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ๊ณผ ์‘๋‹ต์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ

Mocking ์˜์กด ๊ฐ์ฒด

  • @Mock : Mock ๊ฐ์ฒด์˜ ์ธ์Šคํ„ด์Šค ๋‚ด๋ถ€๋Š” ๋น„์–ด์žˆ๋‹ค. (Null)
  • @InjectMock :ํ•ด๋‹น ๊ฐ์ฒด์˜ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋กœ ์กด์žฌํ•˜๋Š” ์˜์กด๋œ ๋‹ค๋ฅธ ๊ฐ์ฒด๋“ค์ด Mockํ˜น์€ Spy๋กœ ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋ผ๋ฉด ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณต
@InjectMocks
    private FollowService followService;

    @Mock
    private UserRepository userRepository;

    @Mock
    private FollowRepository followRepository;

Junit

@BeforeEach

  • ๋งค ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ์ดˆ๊ธฐํ™”๋˜์–ด์•ผ ํ•˜๋Š” ํด๋ž˜์Šค ๋“ฑ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
  • ๋ชจ๋“  ํ…Œ์ŠคํŠธ์— ํ•œ ๋ฒˆ๋งŒ ๋กœ๋”ฉ๋˜์–ด์•ผ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด, @BeforeAll ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ค‘๋ณต์„ ์ค„์ผ ์ˆ˜ ์žˆ์Œ.
@BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this); // Mockito ์ดˆ๊ธฐํ™”

        // ํ…Œ์ŠคํŠธ์šฉ ์‚ฌ์šฉ์ž ๋ฐ ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ฑ
        user = User.builder()
                .userId(userId)
                .username(userName)
                .build();

        post = Post.builder()
                .postId(postId)
                .caption("Sample Post Content")
                .build();

assertions

 assertNotNull(savedComment);
        assertEquals(request.getComment(), savedComment.getComment());
        assertEquals(user, savedComment.getUser());
        assertEquals(post, savedComment.getPost());
  • assertEquals
  • ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฐ’๊ณผ ์‹ค์ œ ๊ฐ’์ด ๋™์ผํ•œ ์ง€ ๊ฒ€์‚ฌ
  • ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜ :expected. ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.
  • ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜ : actual. ์‹ค์ œ ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.

About

CEOS 20th BE study - instagram clone coding

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •