๊ธฐ์กด Monolithicํ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ, ์ข ์์ ์ธ ์๋น์ค ๊ณ์ธต๊ณผ ํ์ ๊ณผ์ ์์ Branch๊ฐ ์ํค๋ ๊ฒฝํ์ ํตํด ๋ ๋ฆฝ์ ์ธ ์ํคํ ์ฒ ํ๊ฒฝ์ ๋ํ ๊ด์ฌ์ผ๋ก Microservice Architecture ํ๊ฒฝ์ ํ๋ก์ ํธ๋ฅผ ์งํํ์์ต๋๋ค.
- OO๋ํ๊ต ์ฌํ์ ๋์ ์ถ๊ตฌ ๋์๋ฆฌ๋ฅผ ๋ง๋ค๊ณ ์ถ์ ๋ ๐ฒ
- ๋์๋ฆฌ๊ฐ ๊ฒฝ๊ธฐ ์ผ์ ์ ์ก๊ณ FIFA์ฒ๋ผ ์ค์ฟผ๋๋ฅผ ์ง๊ณ ์ถ์ ๋ ๐
- ๋์๋ฆฌ ์ผ์ ๊ด๋ฆฌ๋ฅผ ์์ํ๊ฒ ํ๊ณ ์ถ์ ๋ ๐พ
โจ ์ถ๊ตฌ ๋์๋ฆฌ ๊ด๋ฆฌ ์๋น์ค, ํ๋ณผํ๋ ์ฆ ์
๋๋ค! ๐ฅณ
- ๋ฉ์ธ ํ(๋ณธ์ธ ์ผ์ ๋ฐ ์ ๋ณด ํ์ธ) โพ
- ๊ตฌ๋จ ๊ฐ์ ๋ฐ ๊ฒ์ ๐
- ๊ตฌ๋จ์ฅ์ ๊ฐ์ ์ ์ฒญ(Role ๋ถ์ฌ) ๐ฌ
- ์ผ์ ์์ฑ ๋ฐ ์กฐํ ๐
- ์ค์ฟผ๋ ์์ฑ ๋ฐ ์กฐํ ๐
- ํ์ ์ ๋ณด ์กฐํ ๐ฆ
| ๋ฉ์ธ ํ(๋ณธ์ธ ์ผ์ ๋ฐ ์ ๋ณด ํ์ธ) | ๊ตฌ๋จ ๊ฐ์ ๋ฐ ๊ฒ์ | ๊ตฌ๋จ์ฅ์ ๊ฐ์ ์ ์ฒญ(Role ๋ถ์ฌ) |
|---|---|---|
![]() |
![]() |
![]() |
| ์ผ์ ์์ฑ ๋ฐ ์กฐํ | ์ค์ฟผ๋ ์์ฑ ๋ฐ ์กฐํ | ํ์ ์ ๋ณด ์กฐํ |
![]() |
![]() |
![]() |
Jwt Token Storage (Cookie? Session? Redis? Memcached?)
Access Token์ ์ ํจ๊ธฐ๊ฐ์ ์งง๊ฒํ์ฌ ๋ณด์๋ ๋์ด๊ณ , ํธ์์ฑ๋ ์ฑ๊ธฐ๋ ๋ฐฉ๋ฒ์ด๋ค. ๋ก๊ทธ์ธ์ ์๋ฃํ๋ฉด, ์ ํจ๊ธฐ๊ฐ์ด ์งง์ Access Token๊ณผ ์ ํจ๊ธฐ๊ฐ์ด ๊ธด Refresh Token์ ๋ฐ๊ธํด์ค๋ค.
Access Token์ ๊ธฐ์กด์ ์ฌ์ฉํ๋ JWT ํ ํฐ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๊ณ , Refresh Token์ Access Token์ด ๋ง๋ฃ๋์์ ๋, ์๋ก ๋ฐ๊ธํด์ฃผ๋ ํ ํฐ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
Access Token ๋ง๋ฃ์๊ฐ์ ์งง๊ฒ ํ๋ฉด ๋ณด์์ฑ์ ์ข์์ง๋๋ค. ๊ทธ๋ฌ๋, Access Token์ ๋ง๋ฃ์๊ฐ์ ์งง๊ฒ ๊ฐ์ ธ๊ฐ๋ฉด ์ฌ์ดํธ๋ฅผ ์ด์ฉํ๋ ํ์์ ์์ฃผ ๋ก๊ทธ์ธ ํด์ผ๋๋ ๋ถํธํจ์ด ์์ต๋๋ค.
๋ฐ๋ผ์, Refresh Token์ ์ด์ฉํ์ฌ Access Token์ ์ฌ๋ฐ๊ธํ ์ ์๊ณ Access Token์ ์ ํจ ๊ธฐ๊ฐ์ ์งง๊ณ ์์ฃผ ์ฌ๋ฐ๊ธ ํ๋๋ก ๋ง๋ค์ด ๋ณด์์ ๊ฐํํ๋ฉด์ ์ฌ์ฉ์๋ ๋ก๊ทธ์์ ๋์ด ๋ค์ ๋ก๊ทธ์ธํด์ผ ๋๋ ์ํฉ์ ์ฃผ์ง ์๋๋ก ํ๊ธฐ ์ํจ์
๋๋ค.
Refresh Token์ Access Token์ ์ฌ๋ฐ๊ธํ๊ธฐ ์ํ ์ฉ๋์
๋๋ค.
Refresh Token์ ์ฟ ํค์ ์ ์ฅํ๋ฉด ์คํ๋ ค ๋ณด์์ฑ๋ง ๋จ์ด๋จ๋ฆฌ๋ ํ์๊ฐ ๋ฉ๋๋ค. ์ฟ ํค๋ CSRF ๊ณต๊ฒฉ์ ์ทจ์ฝํ๋ค๋ ์ ์ ๊ฐ์ง๊ณ ์์ด ์ข์ง ์์ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ๊ฒฐ๋ก ์ ๋ด๋ ธ์ต๋๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก Refresh Token์ ์ธ์
์คํ ๋ฆฌ์ง์ ์ ์ฅํ๋ ๊ฒ๋ XSS ๊ณต๊ฒฉ์ ์ทจ์ฝ์ฑ์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
๋ฐ๋ผ์ Refresh Token์ Redis์ ์ ์ฅํ๋ ๋ฐฉ์์ ์ฑํํ์ต๋๋ค. ๊ทธ ์ด์ ๋
- Key - Value ๋ฐฉ์, ์ธ๋ฉ๋ชจ๋ฆฌ DB ๋ฐฉ์์ผ๋ก ๋น ๋ฅด๊ฒ ์ ๊ทผํ ์ ์์ต๋๋ค.
- ๋ธ๋ผ์ฐ์ ์ ๋นํด ํ์ทจ ๊ฐ๋ฅ์ฑ์ด ๋ฎ๋ค๊ณ ์๊ฐํ๋ redis ์๋ฒ์ ์ ์ฅํ๋ ๋ฐฉ์์ ๋๋ค.
- Refresh Token์ ์๊ตฌ์ ์ผ๋ก ์ ์ฅ๋๋ ๋ฐ์ดํฐ๊ฐ ์๋๋๋ค.
๋ ๋์ค๋ key-value ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ ๋ฐ์ดํฐ ์คํ ๋ฆฌ์ง์
๋๋ค. ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์(๋ฉ์ธ ๋ฉ๋ชจ๋ฆฌ์ธ RAM) ์ ์ฅํ๊ณ ์กฐํํ๋ in-memory ๋ฐ์ดํฐ๋ฒ ์ด์ค์
๋๋ค.
Memcached ๋ผ๋ ์ธ๋ฉ๋ชจ๋ฆฌ ๋ฐ์ดํฐ ์คํ ๋ฆฌ์ง๋ ์์ง๋ง, ์ฑ๋ฅ์ฐจ์ด๊ฐ ํฌ๊ฒ ์๊ณ , Memcached๋ ๋ฌธ์์ด๋ง ์ง์ํ๊ธฐ ๋๋ฌธ์ Redis๋ฅผ ์ ํํ์ต๋๋ค.
2. Gateway-Server JwtToken (Pre)Filter ์ ์ฉ
MSA ํ๊ฒฝ์์ JwtTokenFilter๋ฅผ ์ ์ฉํ๋ ๊ณผ์ ์ Gateway-Server์์ ์์ํฉ๋๋ค. ์ด ๊ณผ์ ์์๋ **Filter๋ฅผ ์ ์ฉํ์ฌ ๋ชจ๋ ์์ฒญ์ด ์ ํจํ JWT ํ ํฐ์ ๊ฐ์ง๊ณ ์๋์ง ๊ฒ์ฆ**ํฉ๋๋ค. ๊ฒ์ฆ์ ํต๊ณผํ ์์ฒญ๋ง์ด ๋ด๋ถ ์๋น์ค๋ก ์ ๋ฌ๋๋ฉฐ, ์ด๋ ๋ณด์์ ๊ฐํํ๊ณ ์๋น์ค ๊ฐ์ ์์ ํ ํต์ ์ ๋ณด์ฅํฉ๋๋ค. ๋ํ, Gateway-Se! rver๋ ๋ก๋ ๋ฐธ๋ฐ์ฑ๋ ๋ด๋นํ์ฌ, ์์ฒญ์ ์ฌ๋ฌ ์ธ์คํด์ค์ ๊ท ๋ฑํ๊ฒ ๋ถ๋ฐฐํฉ๋๋ค. ์ด๋ฌํ ๊ณผ์ ์ ํตํด ์์คํ ์ ์์ ์ฑ๊ณผ ์ฒ๋ฆฌ ๋ฅ๋ ฅ์ ๋์ด๋ฉฐ, MSA ํ๊ฒฝ์์์ ์๋น์ค ์ด์์ ์ต์ ํํฉ๋๋ค.
์์ฒญ์ด ๋ค์ด์ค๋ฉด, ๋งคํ์ ํตํด ํ๋ ๋์ผ์ดํธ์์ ํด๋น ์์ฒญ์ด ์ฒ๋ฆฌ๋ ์กฐ๊ฑด์ ํ๋จํฉ๋๋ค. ์ดํ, ์์ ์คํ ์ ์ **์ฌ์ ํํฐ(Pre Filter)๋ฅผ ํต๊ณผ**ํด์ผ ํ๋ฉฐ, ์ด๋ ์์ฒญ์ ๋ํ ์ด๊ธฐ ์ฒ๋ฆฌ๋ ๊ฒ์ฆ์ ๋ด๋นํฉ๋๋ค. ์กฐ๊ฑด์ ๋ถํฉํ๋ ์๋น์ค๊ฐ ์คํ๋์ด, ์์ฒญ์ ๋ํ ์ค์ ๋ก์ง์ด ์ฒ๋ฆฌ๋ฉ๋๋ค. ์์ ์ด ์ข ๋ฃ๋ ํ์๋ ํ์ ํํฐ(Post Filter)๋ฅผ ํต๊ณผํ๊ฒ ๋๋๋ฐ, ์ด๋ ์๋ต์ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋ด๊ธฐ ์ ์ ํ์ํ ์ฒ๋ฆฌ๋ฅผ ์ํํฉ๋๋ค. ํํฐ๋ ํ๋กํผํฐ ํ์ผ์ด๋ ์๋ฐ ์ฝ๋๋ฅผ ํตํด ์ ์ํ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ ํตํด ์์ฒญ๊ณผ ์๋ต์ ํ๋ฆ์ ์ ์ฐํ๊ฒ ๊ด๋ฆฌํ ์ ์์ต๋๋ค. ๋ง์ง๋ง์ผ๋ก, ์ฒ๋ฆฌ๋ ์๋ต์ ๋งคํ์ ๊ฑฐ์ณ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌ๋ฉ๋๋ค. ์ด ๊ณผ์ ์ ํตํด, Spring Cloud Gateway๋ ๋ค์ํ ์์ฒญ์ ๋ํด ์กฐ๊ฑด๋ถ ๋ก์ง ์คํ, ์ฌ์ ๋ฐ ์ฌํ ์ฒ๋ฆฌ๋ฅผ ํตํ ์ธ๋ฐํ ์์ฒญ/์๋ต ๊ด๋ฆฌ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
3. Update ๋ก์ง ์์ (JPQL to JPA Dirty Checking)
๋ฌธ์ ์ํฉ: ํ์ ์ ๋ณด ์์ ๋ก์ง์ ๊ตฌํํ ๋ @Modifying ์ด๋ ธํ ์ด์ ์ ํ์ฉํ์ฌ Update ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ์ฌ ์์ ํ๋๋ก Repository์์ ์ฝ๋๋ฅผ ๊ตฌํ
JPA๋ฅผ ์ฌ์ฉํ ๋ ๋ํฐ ์ฒดํน(Dirty Checking)์ ํ์ฉํ๋ ๊ฒ์ ๋งค์ฐ JPA์ค๋ฌ์ด ์ ๊ทผ ๋ฐฉ์์
๋๋ค. ๋ํฐ ์ฒดํน์ ์ํฐํฐ์ ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋ ์ด๋ฅผ ์๋์ผ๋ก ๊ฐ์งํ๊ณ ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ํ๋ JPA์ ํต์ฌ ๊ธฐ๋ฅ ์ค ํ๋์
๋๋ค. ์ด ๊ณผ์ ์ ํธ๋์ญ์
์ด ์ปค๋ฐ๋๋ ์์ ์ ์คํ๋๋ฉฐ, ๋ณ๊ฒฝ๋ ์ํฐํฐ์ ์ค๋
์ท๊ณผ ์๋ณธ ์ํฐํฐ๋ฅผ ๋น๊ตํ์ฌ ์๋์ผ๋ก UPDATE ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์คํํฉ๋๋ค.
๋ํฐ ์ฒดํน์ ์ด์ฉํ๋ฉด, ๊ฐ๋ฐ์๋ ์ํฐํฐ์ ์ํ๋ฅผ ์ง์ ๊ด๋ฆฌํ๊ณ ์ ์ ํ ์์ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ํ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ํ์๊ฐ ์์ต๋๋ค. ์ด๋ ์ฝ๋์ ๋ณต์ก์ฑ์ ์ค์ด๊ณ , ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ ๋ฎ์ถ๋ฉฐ, ๊ฐ๋ฐ์๊ฐ ๋น์ฆ๋์ค ๋ก์ง์ ๋ ์ง์คํ ์ ์๊ฒ ํด์ค๋๋ค. ๋ํ, ํธ๋์ญ์
์ปค๋ฐ ์์ ์ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์์ ์์ธ ์ฟผ๋ฆฌ๋ค์ด ์ผ๊ด์ ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ์ ์ก๋๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ ์ธก๋ฉด์์๋ ์ด์ ์ด ์์ต๋๋ค.
Spring Data JPA์์๋ @Query ์ ๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์ง์ ์ ์ํ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ์ ์์ต๋๋ค. ํนํ, ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ ์๋ฐํ๋ INSERT, DELETE, UPDATE ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ๋๋ @Modifying ์ ๋ ธํ ์ด์ ์ ํจ๊ป ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์ด ์กฐํฉ์ ์ฌ์ฉํ๋ฉด JPA์ ๋ณ๊ฒฝ ๊ฐ์ง ๊ธฐ๋ฅ์ ๊ฑด๋๋ฐ๊ณ , ์ฟผ๋ฆฌ ์คํ์ ๋ ํจ์จ์ ์ผ๋ก ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ํน์ ์ฌ์ฉ์์ ์ด๋ฆ์ ์ ๋ฐ์ดํธํ๋ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ ์ ์์ต๋๋ค:
@Transactional
@Modifying
@Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
int updateUserName(@Param("id") Long id, @Param("name") String name);
์ฌ๊ธฐ์ @Transactional์ ํด๋น ๋ฉ์๋์ ์คํ์ ํธ๋์ญ์ ๋ฒ์ ๋ด์์ ์ฒ๋ฆฌํ๊ฒ ๋ค๋ ๊ฒ์ ๋ํ๋ด๋ฉฐ, @Modifying์ ๋ณ๊ฒฝ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ๊ฒ์์ ๋ช ์ํฉ๋๋ค. @Query๋ ์คํํ JPQL ์ฟผ๋ฆฌ๋ฅผ ์ ์ํ๊ณ , @Param์ ์ฟผ๋ฆฌ์ ์ ๋ฌ๋ ํ๋ผ๋ฏธํฐ๋ฅผ ์ง์ ํฉ๋๋ค.
๋ํ, JPA์์๋ ๋ฒํฌ ์ฐ์ฐ์ ์ง์ํฉ๋๋ค. ๋ฒํฌ ์ฐ์ฐ์ด๋, ๋จ์ผ ๋ฐ์ดํฐ๊ฐ ์๋ ๋๋์ ๋ฐ์ดํฐ์ ๋ํ UPDATE, DELETE ์์ ์ ํ ๋ฒ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋๋์ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์ผ๋ฉฐ, ์ฑ๋ฅ ๊ฐ์ ์๋ ํฌ๊ฒ ๊ธฐ์ฌํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ๋ชจ๋ ์ฌ์ฉ์์ ๋์ด๋ฅผ ํ ์ด์ฉ ์ฆ๊ฐ์ํค๊ณ ์ ํ ๋ ๋ค์๊ณผ ๊ฐ์ด ํ ์ ์์ต๋๋ค:
@Transactional
@Modifying
@Query("UPDATE User u SET u.age = u.age + 1")
int incrementAllUserAges();
์ด ์ฝ๋๋ ๋ชจ๋ ์ฌ์ฉ์์ ๋์ด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํ ๋ฒ์ ์ ๋ฐ์ดํธํ๊ณ , ๋ณ๊ฒฝ๋ ํ์ ์๋ฅผ ๋ฐํํฉ๋๋ค. @Transactional๊ณผ @Modifying์ ์ฌ์ฉํจ์ผ๋ก์จ, JPA๋ฅผ ํตํด ํจ์จ์ ์ผ๋ก ๋ฒํฌ ์ฐ์ฐ์ ์ํํ ์ ์๊ฒ ๋ฉ๋๋ค. ์ด ๋ฐฉ์์ **๋ฐ์ดํฐ ์ฒ๋ฆฌ ์์ ์ ๋๊ท๋ชจ๋ก ์งํํ ๋ ํนํ ์ ์ฉ**ํ๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ ์ต์ ํ์๋ ํฌ๊ฒ ๊ธฐ์ฌํ ์ ์์ต๋๋ค.
4. SpringBoot + Kafka ์ฐ๋
์นดํ์นด(Kafka)๋ ์น์ฌ์ดํธ, ์ดํ๋ฆฌ์ผ์ด์
, ์ผ์ ๋ฑ์์ ์์ง๋ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ ์กํ๊ธฐ ์ํด ์ค๊ณ๋ ๋ถ์ฐ ์คํธ๋ฆฌ๋ฐ ํ๋ซํผ์
๋๋ค. ์ด ํ๋ซํผ์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ์ดํ๋ฆฌ์ผ์ด์
๊ณผ ๋ฐ์ดํฐ๋ฅผ ์๋นํ๋ ์ดํ๋ฆฌ์ผ์ด์
์ฌ์ด์์ ์ค์ฌ์ ์ญํ ์ ํ๋ฉฐ, ๋ฐ์ดํฐ์ ์ ์ก, ์ฒ๋ฆฌ, ๊ด๋ฆฌ๋ฅผ ๋ด๋นํฉ๋๋ค. ์นดํ์นด ์์คํ
์ ์ฌ๋ฌ ์์(๋
ธ๋)๋ก ๊ตฌ์ฑ๋ ์ ์์ผ๋ฉฐ, ์ด๋ฅผ '์นดํ์นด ํด๋ฌ์คํฐ'๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
์ด ์์คํ
์ ๋ค๋ฅธ ๋ฉ์์ง ์์คํ
๊ณผ ์ ์ฌํ๊ฒ ์ดํ๋ฆฌ์ผ์ด์
๊ณผ ์๋ฒ ๊ฐ์ ๋น๋๊ธฐ ๋ฐ์ดํฐ ๊ตํ์ ์ฉ์ดํ๊ฒ ํฉ๋๋ค. ๋ํ, ์นดํ์นด๋ ํ๋ฃจ์ ์์กฐ ๊ฐ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ ๋ฅ๋ ฅ์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๊ฐ๋จํ ๋งํด, ์นดํ์นด๋ ๋ค์ํ ์๋น์ค๋ก๋ถํฐ ๋์ค๋ ๋ฐ์ดํฐ ํ๋ฆ์ ์ค์๊ฐ์ผ๋ก ์ ์ดํ๊ณ , ์ด๋ฅผ ํตํด ์๋น์ค ๊ฐ ์ฐ๊ฒฐ์ ๊ฐ๋ฅํ๊ฒ ํ๋ ์ค์ถ์ ์ธ ์ญํ ์ ํ๋ ํ๋ซํผ์
๋๋ค. ์ด๋ฅผ ํตํด ๋ณต์กํ ๋ฐ์ดํฐ ํ๊ฒฝ์์๋ ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์คํธ๋ฆผ ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํด์ง๋๋ค.
โ
โถ Cluster : ์ฌ๋ฌ ๋์ ์ปดํจํฐ๋ค์ด ์ฐ๊ฒฐ๋์ด ํ๋์ ์์คํ
์ฒ๋ผ ๋์ํ๋ ์ปดํจํฐ๋ค์ ์งํฉ
โถ Producer : ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด๋ด์ด ์ ๋ฌํ๋ ์ ๋ฌ์์ ์ญํ
โถ Consumer : ํ๋ก๋์์์ ์ ๋ฌํ ๋ฐ์ดํฐ๋ฅผ ๋ธ๋ก์ปค์ ์์ฒญํ์ฌ ๋ฉ์์ง(๋ฐ์ดํฐ)๋ฅผ ์๋นํ๋ ์ญํ
โถ Broker : ์์ฐ์์ ์๋น์์์ ์ค์ฌ์ ์ญํ ์ ํ๋ ์ญํ
โถ Topic : ๋ณด๋ด๋ ๋ฉ์์ง๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํ ์นดํ
๊ณ ๋ฆฌํ
์นดํ์นด(Kafka)๋ ๊ธฐ๋ณธ์ ์ผ๋ก listener๋ฅผ ํตํด producer๋ก๋ถํฐ์ ์์ฒญ์ ๋ฐ์ ์ฒ๋ฆฌํ๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋๋ค. ์ด ์์คํ
์์ 'KafkaServer'๋ broker ์ญํ ์ ํ๋ฉฐ, producer์ consumer๋ ์นดํ์นด๊ฐ ์ ๊ณตํ๋ API๋ฅผ ํตํด ๊ตฌํ๋ ์ดํ๋ฆฌ์ผ์ด์
์ ์๋ฏธํฉ๋๋ค.
์นดํ์นด ํด๋ฌ์คํฐ๋ ํ๋ ์ด์์ broker๋ก ๊ตฌ์ฑ๋ ์ ์์ต๋๋ค. ํด๋ฌ์คํฐ ๋ด์ ๊ฐ KafkaServer(broker)๋ ๊ณ ์ ํ ์๋ณ์์ธ 'broker.id'๋ฅผ ๋ถ์ฌ๋ฐ์ต๋๋ค. ๋ํ, ์ด๋ฌํ broker๋ producer๋ก๋ถํฐ ์์ฑ๋ ๋ฉ์์ง๋ฅผ ์ ์ฅํ ์์น ์ ๋ณด์ ํด๋ฌ์คํฐ์ ๋ฉํ์ ๋ณด๋ฅผ ์ ์ฅ ๋ฐ ๊ด๋ฆฌํ๊ธฐ ์ํด Zookeeper์ ์ฐ๊ฒฐ๋ฉ๋๋ค.
Kafka Cluster๋ ์ฌ๋ฌ ๋ธ๋ก์ปค๋ค์ ์ ๋ณด๋ฅผ ๊ด๋ฆฌํ๊ณ ํจ๊ณผ์ ์ธ ๋ฆฌ๋ ์ ์ถ(leader election)์ ์ํด Zookeeper๋ฅผ ํ์ฉํฉ๋๋ค. ํน์ broker์ ์ฅ์ ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ, ์ปจํธ๋กค๋ฌ๋ ๋ณ๊ฒฝ๋ ๋ฆฌ๋ ํํฐ์
์ ์ ๋ณด๋ฅผ ์
๋ฐ์ดํธํ๊ธฐ ์ ์ ๊ทธ ์ ๋ณด๋ฅผ Zookeeper์ ์ ์ฅํ๋ ๋ฐฉ์์ผ๋ก ์ด์๋ฉ๋๋ค. ์ด๋ฌํ ๊ตฌ์กฐ๋ ์นดํ์นด ํด๋ฌ์คํฐ๊ฐ ๋์ ๊ฐ์ฉ์ฑ๊ณผ ์ ๋ขฐ์ฑ์ ์ ์งํ ์ ์๋๋ก ๋์์ค๋๋ค.
์นดํ์นด(Kafka)์์ ์ด๋ฒคํธ ์คํธ๋ฆผ์ 'ํ ํฝ(Topic)'์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ์ ์ฅ๋ฉ๋๋ค. ์นดํ์นด์ ํ ํฝ์ ๊ตฌ์ฒดํ๋ ์ด๋ฒคํธ ์คํธ๋ฆผ์ ์๋ฏธํ๋ฉฐ, ์ฐ๊ด๋ ์ด๋ฒคํธ๋ค์ ๋ฌถ์ด ์์ํํ๋ ์ญํ ์ ํฉ๋๋ค. ์ด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ์ด๋ ํ์ผ ์์คํ
์ ํด๋์ ๋น์ ํ ์ ์์ต๋๋ค.
ํ ํฝ์ ์นดํ์นด์์ ์์ฐ์(Producer)์ ์๋น์(Consumer)๋ฅผ ๋ถ๋ฆฌํ๋ ์ค์ํ ๊ฐ๋
์
๋๋ค. Producer๋ ์นดํ์นด์ ํ ํฝ์ ๋ฉ์์ง๋ฅผ ์ ์ฅ(push)ํ๊ณ , Consumer๋ ์ ์ฅ๋ ๋ฉ์์ง๋ฅผ ์ฝ์ด(pull)์ต๋๋ค. ํ๋์ ํ ํฝ์๋ ์ฌ๋ฌ Producer์ Consumer๊ฐ ์กด์ฌํ ์ ์์ต๋๋ค.
์ด๋ฌํ ๊ฐ๋
์ ๊ฐ๋จํ ์ค๋ช
ํ๋ฉด, ๊ด๋ จ๋ ์ด๋ฒคํธ๋ค์ด ๋ชจ์ฌ ์คํธ๋ฆผ์ ํ์ฑํ๊ณ , ์ด ์คํธ๋ฆผ์ด ์นดํ์นด์ ์ ์ฅ๋ ๋ ํ ํฝ์ ์ด๋ฆ์ผ๋ก ์ ์ฅ๋ฉ๋๋ค. ์ด๋ฌํ ๊ณผ์ ์ ํตํด ์นดํ์นด๋ ๋๋์ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋ฉ๋๋ค.
์์์ ์ค๋ช
ํ ์นดํ์นด์ ํ ํฝ๋ค์ ์ฌ๋ฌ ํํฐ์
์ผ๋ก ๋๋ ์ง๋๋ค. ํ ํฝ์ด ์นดํ์นด์์ ์ผ์ข
์ ๋
ผ๋ฆฌ์ ์ธ ๊ฐ๋
์ด๋ผ๋ฉด, ํํฐ์
์ ํ ํฝ์ ์ํ ๋ ์ฝ๋๋ฅผ ์ค์ ์ ์ฅ์์ ์ ์ฅํ๋ ๊ฐ์ฅ ์์ ๋จ์์
๋๋ค. ๊ฐ๊ฐ์ ํํฐ์
์ Append-Only ๋ฐฉ์์ผ๋ก ๊ธฐ๋ก๋๋ ํ๋์ ๋ก๊ทธ ํ์ผ์
๋๋ค.
ํ์ํํด API ๊ฐ์ ๊ฒฝ์ฐ, User-Server์์ ํ์์ ํํดํ๋ฉด Team-Server์ ์กด์ฌํ๋ UserSquad Table์์ ์ญ์ ํด์ผํฉ๋๋ค. ์ด ์ํฉ์์ Kafka๋ฅผ ์ฌ์ฉํ์ฌ ํ์ ํํด ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒฝ์ฐ, User-Server๊ฐ Kafka์ Producer ์ญํ ์ ํ๊ณ , Team-Server๊ฐ Consumer ์ญํ ์ ํฉ๋๋ค.
- Kafka Config (USER-SERVER)
@Bean
public Map<String, Object> UserProducerConfig() {
return CommonJsonSerializer.getStringObjectMap(BOOTSTRAP_SERVERS_CONFIG);
}
@Bean
public ProducerFactory<String, Long> deleteUserProducerFactory() {
return new DefaultKafkaProducerFactory<>(UserProducerConfig());
}
@Bean
public KafkaTemplate<String, Long> deleteUserKafkaTemplate(){
return new KafkaTemplate<>(deleteUserProducerFactory());
}
- UserKafkaProducer
@Component
@RequiredArgsConstructor
public class UserKafkaProducer {
private final KafkaTemplate<String, Long> deleteUserKafkaTemplate;
public void deleteUser(Long userId) {
deleteUserKafkaTemplate.send("user", userId);
}
- USER-SERVICE
@Transactional
public void deleteUser(String studentId) {
User user = userRepository.findByStudentId(studentId)
.orElseThrow(() -> new NotFoundException(NOT_FOUND_STATUS_CODE, NOT_REGISTER_USER_EXCEPTION_MESSAGE));
userRepository.delete(user);
userKafkaProducer.deleteUser(user.getId());
}
- Kafka Config
@Bean
public Map<String, Object> ConsumerConfigs() {
return CommonJsonDeserializer.getStringObjectMap(BOOTSTRAP_SERVERS_CONFIG, GROUP_ID_CONFIG);
}
@Bean
public ConsumerFactory<String, Long> deleteUserSquadConsumerFactory() {
return new DefaultKafkaConsumerFactory<>(ConsumerConfigs());
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, Long> deleteUserSquadListener() {
ConcurrentKafkaListenerContainerFactory<String, Long> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(deleteUserSquadConsumerFactory());
return factory;
}
- TEAM-SERVICE
@Transactional
@KafkaListener(topics = "user", groupId = "group_2")
public void deleteUserSqaud(Long userId) {
userSquadRepository.deleteAllByUserIds(userId);
}
| Role | Name | Github |
|---|---|---|
| BE | ๋ฐ์ข ํ | https://github.com/euics |
| BE | ๋ณ์์ฑ | https://github.com/bes99 |
| BE | ์ด์ฌํ | https://github.com/jaepyo-Lee |
| Android | ์์ฑ์ฐ | https://github.com/imseongwoo |
| Android | ์ด์คํธ | https://github.com/lyh990517 |
| Android | ๊น์ฐฌํ | https://github.com/1chanhue1 |
| Design | ๋ฐ์ฌ์ |










