Skip to content
beurre-noisettePublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

ย 

History

103 Commits

Folders and files

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

Repository files navigation

CokeZet ์ œ๋กœ์ฝœ๋ผ ์ตœ์ €๊ฐ€ ์•Œ๋ฆผ ์„œ๋น„์Šค

ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

CokeZet์€ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํ˜ธํ•˜๋Š” ์ปค๋จธ์Šค์—์„œ ์ œ๋กœ์ฝœ๋ผ ์ตœ์ €๊ฐ€ ์ •๋ณด์™€ ์นด๋“œ์‚ฌ ํ• ์ธ ํ˜œํƒ์„ ์•Œ๋ ค์ฃผ๋Š” ๋ชจ๋ฐ”์ผ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค. ๋งค์ผ ์ตœ์ €๊ฐ€๋ฅผ ์ฐพ์•„๋‹ค๋‹ˆ๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€ ์—†์ด, ์›ํ•˜๋Š” ์กฐ๊ฑด์˜ ํ• ์ธ ์ •๋ณด๊ฐ€ ๋“ฑ๋ก๋˜๋ฉด ์•Œ๋ฆผ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ด๋‹น ์˜์—ญ

  • ์†Œ์…œ ๋กœ๊ทธ์ธ ์‹œ์Šคํ…œ : ํ”Œ๋žซํผ๋ณ„ ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ (Android: Google/Kakao, iOS: Apple/Kakao)
  • ์•Œ๋ฆผ ๊ธฐ๋Šฅ : ์‚ฌ์šฉ์ž ๋งž์ถคํ˜• ์‹ค์‹œ๊ฐ„ ๊ฐ€๊ฒฉ ์•Œ๋ฆผ ์„œ๋น„์Šค

์‚ฌ์šฉ ๊ธฐ์ˆ  ๋ฐ ์„ ํƒ ์ด์œ 

  • ์–ธ์–ด : Java 21

  • ํ”„๋ ˆ์ž„์›Œํฌ : Spring Boot 3.4.3

  • ORM : Spring Data JPA/Hibernate

    • ํ…Œ์ด๋ธ” ์ค‘์‹ฌ์˜ ๋ชจ๋ธ๋ง์— ์ต์ˆ™ํ•ด์ ธ ์žˆ์—ˆ๋Š”๋ฐ, ๊ฐ์ฒด ์ค‘์‹ฌ์  ์•„ํ‚คํ…์ณ๋ฅผ ์—ฐ์Šตํ•ด๋ณด๊ณ ์ž JPA๋ฅผ ์„ ํƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • JPA๋Š” ๊ฐ์ฒด ๊ฐ„์˜ ๊ด€๊ณ„ ํ‘œํ˜„๊ณผ ๋„๋ฉ”์ธ ๋ชจ๋ธ๋ง์ด ์ง๊ด€์ ์ด๋ฉฐ, ๋ฐ˜๋ณต์ ์ธ SQL ์ž‘์„ฑ ์—†์ด ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
  • ๋ณด์•ˆ : Spring Security, JWT

    • CokeZet๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์œผ๋กœ๋งŒ ์ œ๊ณต๋  ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ ์„ธ์…˜ ํ™œ์šฉ์˜ ํ•œ๊ณ„๋ฅผ ๋А๊ผˆ์Šต๋‹ˆ๋‹ค.
      • ์›น ๋ธŒ๋ผ์šฐ์ €์™€ ๋‹ฌ๋ฆฌ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์€ ์ฟ ํ‚ค๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์„ธ์…˜์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์„ธ์…˜ ID ์ €์žฅ ๋ฐ ์š”์ฒญ ํฌํ•จ ๋กœ์ง์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
      • ์•ฑ์ด ์™„์ „ํžˆ ์ข…๋ฃŒ๋œ ํ›„์—๋„ ์ธ์ฆ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด๋•Œ ์ถ”๊ฐ€์ ์ธ ๋กœ์ง์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
      • ์ถ”ํ›„ ์„œ๋น„์Šค์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ ๊ฒฝ์šฐ ์„ธ์…˜ ์ €์žฅ์†Œ ๊ด€๋ฆฌ ๋ถ€๋‹ด์ด ์ฆ๊ฐ€ํ•  ๊ฒƒ์„ ์šฐ๋ คํ–ˆ์Šต๋‹ˆ๋‹ค.
      • ๋ชจ๋ฐ”์ผ ๋„คํŠธ์›Œํฌ ํŠน์„ฑ ์ƒ ๋ถˆ์•ˆ์ •ํ•œ ์—ฐ๊ฒฐ์ด๋‚˜ ๋†’์€ ์ง€์—ฐ ์‹œ๊ฐ„ ๋“ฑ์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ํ™•๋ฅ ์ด ๋†’๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์œผ๋ฉฐ, ์ด๋Ÿฌํ•œ ๋ชจ๋ฐ”์ผ ๋„คํŠธ์›Œํฌ ํ™˜๊ฒฝ์—์„œ ์„ธ์…˜ ๊ด€๋ฆฌ์˜ ์–ด๋ ค์›€์ด ์šฐ๋ ค๋์Šต๋‹ˆ๋‹ค.
    • ๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ JWT๋ฅผ ์„ธ์…˜์˜ ๋Œ€์•ˆ์œผ๋กœ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
      • JWT์˜ Stateless ์•„ํ‚คํ…์ณ ํŠน์„ฑ์„ ํ™œ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์˜ ์ƒํƒœ๋ฅผ ์ €์žฅํ•  ํ•„์š”๊ฐ€ ์—†๊ณ  ์„œ๋ฒ„ ์ธก ์„ธ์…˜ ์ €์žฅ์†Œ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๋ฉฐ ํ† ํฐ ์ž์ฒด์— ์ €ํฌ๊ฐ€ ์š”๊ตฌํ•  ์ตœ์†Œํ•œ์˜ ์ •๋ณด๋ฅผ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
      • ์ถ”ํ›„ ๋‹ค์ค‘ ์„œ๋ฒ„ ํ™˜๊ฒฝ์œผ๋กœ ํ™•์žฅ์„ ๊ณ ๋ คํ–ˆ์„ ๋•Œ ๋ณ„๋„์˜ ์„ธ์…˜ ๊ณต์œ  ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์—†์ด ์ธ์ฆ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.
      • ํ† ํฐ์„ ์ €์žฅ์†Œ์— ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€ํ•˜๊ณ  ์š”์ฒญ๋งˆ๋‹ค ํ—ค๋”์— ํฌํ•จ์‹œํ‚ค๋Š” ๋ฐฉ์‹์ด ๋ชจ๋ฐ”์ผ ์•ฑ ์•„ํ‚คํ…์ณ์— ์ ํ•ฉํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.
    • global/security/jwt/JwtProvider.java, global/security/jwt/JwtAuthenticationFilter.java์— ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค : MySQL

  • API ๋ฌธ์„œํ™” : SpringDoc OpenAPI 2.8.5 (Swagger UI)

    • ์•ฑ ๊ฐœ๋ฐœ์ž์™€์˜ ์›ํ™œํ•œ ํ˜‘์—…์„ ์œ„ํ•ด ํ‘œ์ค€ํ™”๋˜๊ณ  ์ž๋™ํ™”๋œ ๋ฌธ์„œ ์ƒ์„ฑ์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ๋А๊ปด์„œ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ์จ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ๋ฌธ์„œ๊ฐ€ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์–ด ํ•ญ์ƒ ์ตœ์‹  ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์ข‹์•˜์Šต๋‹ˆ๋‹ค.
  • OAuth : Srping OAuth2 Client, Resource Server

  • JWT ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ :

    • io.jsonwebtoken:jjwt 0.11.5 : ๊ธฐ๋ณธ์ธ JWT ์ƒ์„ฑ ๋ฐ ๊ฒ€์ฆ์— ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • Nimbus JOSE+JWT 9.37.3(for Apple Login) : Apple์˜ JWK ์ฒ˜๋ฆฌ ๋ฐ ECDSA ์„œ๋ช… ๊ฒ€์ฆ์— ํŠนํ™”๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—ฌ์„œ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • Auth0 JWT 4.4.0(for Apple Login) : Apple Client Secret ์ƒ์„ฑ ์‹œ ํ•„์š”ํ•œ ECDSA ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ง€์›ํ•˜์—ฌ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.
    • domain/user/service/apple/AppleJwtKeyService.java, domain/user/service/apple/AppleClientSecretService.java์— ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ ๊ธฐ๋Šฅ ๋ฐ ์„ค๊ณ„ ๊ฒฐ์ • ์ด์œ 

์†Œ์…œ ๋กœ๊ทธ์ธ

ํ”Œ๋žซํผ๋ณ„ ์ตœ์ ํ™”๋œ ์†Œ์…œ ๋กœ๊ทธ์ธ

  • ๊ฐ ํ”Œ๋žซํผ์˜ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ํ”Œ๋žซํผ๋ณ„ ์„ ํ˜ธ ๋กœ๊ทธ์ธ ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • Andorid : Google(๊ธฐ๊ธฐ ์—ฐ๋™์ด ์šฉ์ด) + Kakao(๊ตญ๋‚ด ์‚ฌ์šฉ์ž ์นœํ™”)
    • Apple : Apple(Apple ์ •์ฑ… ์ค€์ˆ˜) + Kakao(๊ตญ๋‚ด ์‚ฌ์šฉ์ž ์นœํ™”)
  • ๋‹จ์ผ ๋ฐฑ์—”๋“œ์—์„œ ๋ชจ๋“  ์ธ์ฆ ๋ฐฉ์‹์„ ์ผ๊ด€๋˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์•„ํ‚คํ…์ณ๋ฅผ ๊ณ ๋ฏผํ–ˆ์Šต๋‹ˆ๋‹ค.
    • domain/user/service/GoogleLoginService.java, domain/user/service/AppleLoginService.java์— ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

SocialLoginService ์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ฐ™์€ ์ „๋žต ํŒจํ„ด์„ ์ ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  • ๊ฐ ์†Œ์…œ ๋กœ๊ทธ์ธ ์ œ๊ณต์ž์˜ ๊ตฌํ˜„ ์„ธ๋ถ€์‚ฌํ•ญ์„ ์บก์Аํ™”ํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ณ ์ž ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ์ƒˆ๋กœ์šด ์†Œ์…œ ๋กœ๊ทธ์ธ ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์„ค๊ณ„๋ฅผ ํ•ด๋ณด๊ณ ์ž ๊ณ ๋ฏผํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • domain/user/service/SocialLoginService.java, domain/user/service/SocialLoginFactory.java์— ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๊ด€๋œ ์ธ์ฆ API ์‘๋‹ต ๊ตฌ์กฐ ์„ค๊ณ„

  • ๋ชจ๋“  ์ธ์ฆ API(์†Œ์…œ ๋กœ๊ทธ์ธ, ํ† ํฐ ๊ฒ€์ฆ)๊ฐ€ ์ผ๊ด€๋œ LoginResponse ํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋‹จ์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ธ์ฆ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ๊ฐœ๋ฐœ ๋ณต์žก์„ฑ์ด ๊ฐ์†Œํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋“  ๋กœ๊ทธ์ธ API์—์„œ accessToken, refreshToken, ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋™์ผํ•œ ๊ตฌ์กฐ๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • domain/user/service/AuthService.java์— ํ† ํฐ ๊ฒ€์ฆ ๋ฐ ๊ฐฑ์‹  ๋กœ์ง์„ ์ค‘์•™ํ™”ํ•˜์—ฌ ์ฝ”๋“œ ์ค‘๋ณต์„ ๋ฐฉ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค.
// AuthService.java์˜ ์ผ๋ถ€๋ถ„
public LoginResponse validateAndRefreshToken(Long userId) {
  User user = userRepository.findById(userId)
          .orElseThrow(() -> new UserNotFoundException("์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."));

  // JWT ์•ก์„ธ์Šค ํ† ํฐ ์ƒ์„ฑ
  String accessToken = jwtProvider.generateAccessToken(user.getId(), user.getEmail(), user.getRole());

  // ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์ƒ์„ฑ (๊ธฐ์กด ํ† ํฐ ์‚ญ์ œ ํ›„ ์ƒˆ๋กœ ์ƒ์„ฑ)
  RefreshToken refreshToken = refreshTokenService.createRefreshToken(user.getId());

  return LoginResponse.builder()
          .accessToken(accessToken)
          .refreshToken(refreshToken.getToken())
          .user(new LoginResponse.UserInfo(
                  user.getId(),
                  user.getEmail(),
                  user.getNickname()
          ))
          .newUser(false)
          .build();
}

๋ชจ๋ฐ”์ผ ์ž๋™ ๋กœ๊ทธ์ธ ์ตœ์ ํ™”

  • ๋ชจ๋ฐ”์ผ ์•ฑ์˜ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ณ ๋ คํ•œ ์ž๋™ ๋กœ๊ทธ์ธ ํ๋ฆ„์„ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

    • ์•ฑ ์žฌ์‹œ์ž‘ ์‹œ GET /api/auth/login์œผ๋กœ ์ €์žฅ๋œ ํ† ํฐ ๊ฒ€์ฆ ๋ฐ ๊ฐฑ์‹ 
    • ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์œผ๋กœ ์ž๋™ ๊ฐฑ์‹  ์ฒ˜๋ฆฌ
    • ๋ชจ๋“  ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ์—๋งŒ ์‚ฌ์šฉ์ž์—๊ฒŒ ์žฌ๋กœ๊ทธ์ธ ์š”์ฒญ
  • ๋งค ๋กœ๊ทธ์ธ ์‹œ๋„๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•˜๋Š” ์ „๋žต์œผ๋กœ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค.

    • ๋ฐœ๊ธ‰๋œ ํ† ํฐ์˜ ์‚ฌ์šฉ ๊ธฐ๊ฐ„์„ ์ตœ์†Œํ™”ํ•˜์—ฌ ํƒˆ์ทจ ์œ„ํ—˜ ๊ฐ์†Œ
    • ํ† ํฐ ๋งŒ๋ฃŒ ๊ด€๋ฆฌ์˜ ๋ณต์žก์„ฑ ๋Œ€์‹  ๋ณด์•ˆ์„ฑ ์šฐ์„  ์ ‘๊ทผ
    • API ํ˜ธ์ถœ ํŒจํ„ด ๋ถ„์„์„ ํ†ตํ•œ ์ตœ์ ์˜ ํ† ํฐ ๊ฐฑ์‹  ์ „๋žต ์ˆ˜๋ฆฝ

Refresh Token Rotation ์ •์ฑ…์„ ์ทจํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ํ† ํฐ์ด ํƒˆ์ทจ๋˜์–ด๋„ ํ•œ ๋ฒˆ ์‚ฌ์šฉ ํ›„์—๋Š” ๋ฌดํšจํ™”๋˜์–ด ๋ณด์•ˆ ์œ„ํ—˜์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ ํ† ํฐ ์žฌ์‚ฌ์šฉ ๊ฐ์ง€ ์‹œ ๋ชจ๋“  ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋ฌดํšจํ™”ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ถ”๊ฐ€ ๋ณด์•ˆ ์กฐ์น˜๋ฅผ ์ทจํ–ˆ์Šต๋‹ˆ๋‹ค.
  • domain/user/service/RefreshTokenService.java, domain/user/model/RefreshToken.java์— ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ† ํฐ ์žฌ์‚ฌ์šฉ ๊ฐ์ง€ ๋กœ์ง์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฆฌํ”„๋ ˆ์‰ฌ ํ† ํฐ ํƒˆ์ทจ ์‹œ๋„๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ง€ํ•˜์—ฌ ์ฆ‰๊ฐ์ ์ธ ๋Œ€์‘์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ๋‹จ์ˆœํ•œ ๋งŒ๋ฃŒ ์ฒ˜๋ฆฌ๊ฐ€ ์•„๋‹Œ ์‚ฌ์šฉ ์—ฌ๋ถ€๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ธฐ๋กํ•˜์—ฌ ์ถ”๊ฐ€์ ์ธ ๋Œ€์‘ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • global/error/exception/TokenReuseDetectedException.java, domain/user/service/RefreshTokenService.java์— ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์ˆ ์  ๋„์ „๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ๋ฐ ์ด์œ 

1. Apple ๋กœ๊ทธ์ธ ๊ตฌํ˜„์˜ ๋ณต์žก์„ฑ

๋ฌธ์ œ ์ƒํ™ฉ:

  • Apple์˜ OAuth 2.0 ๊ตฌํ˜„์€ Google๋ณด๋‹ค ํ›จ์”ฌ ๋ณต์žกํ•œ ๊ฒ€์ฆ ๊ณผ์ •์„ ์š”๊ตฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ID ํ† ํฐ ๊ฒ€์ฆ์„ ์œ„ํ•ด Apple์˜ ๊ณต๊ฐœํ‚ค(JWK)๋ฅผ ๊ฐ€์ ธ์™€ ECDSA ์„œ๋ช…์„ ๊ฒ€์ฆํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • Apple ์„œ๋ฒ„์™€์˜ ํ†ต์‹  ๋นˆ๋„๊ฐ€ ๋†’์•„์งˆ ๊ฒฝ์šฐ API ํ˜ธ์ถœ ์ œํ•œ์— ๋„๋‹ฌํ•  ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ํ‚ค ๊ด€๋ฆฌ์™€ ํ† ํฐ ๊ฒ€์ฆ ๋กœ์ง์˜ ๋ณต์žก๋„๊ฐ€ ๋†’์•˜์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • JWK ์บ์‹ฑ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ๊ตฌํ˜„: AppleJwtKeyService ํด๋ž˜์Šค์—์„œ Apple ๊ณต๊ฐœํ‚ค๋ฅผ ๊ฐ€์ ธ์™€ ๋ฉ”๋ชจ๋ฆฌ์— ์บ์‹ฑํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
// AppleJwtKeyService.java์˜ ์ผ๋ถ€๋ถ„
private void fetchAndCacheApplePublicKeys() {
    try {
        String response = restTemplate.getForObject(APPLE_PUBLIC_KEYS_URL, String.class);
        JWKSet jwkSet = JWKSet.parse(response);
        for (JWK jwk : jwkSet.getKeys()) {
            publicKeys.put(jwk.getKeyID(), jwk);
        }
        log.info("Apple ๊ณต๊ฐœ ํ‚ค ์บ์‹ฑ ์™„๋ฃŒ: {} ๊ฐœ์˜ ํ‚ค", publicKeys.size());
    } catch (Exception e) {
        log.error("Apple ๊ณต๊ฐœ ํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ ์‹คํŒจ", e);
        throw new RuntimeException("Apple ๊ณต๊ฐœํ‚ค๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.", e);
    }
}
  • ํ† ํฐ ๊ฒ€์ฆ ๋กœ์ง ์ตœ์ ํ™”: Nimbus JOSE+JWT ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ID ํ† ํฐ์˜ ํ—ค๋”์—์„œ kid(Key ID)๋ฅผ ์ถ”์ถœํ•˜๊ณ , ์บ์‹ฑ๋œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ด ์„œ๋ช…์„ ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
// AppleLoginService.java์˜ ์ผ๋ถ€๋ถ„
SignedJWT signedJWT = SignedJWT.parse(idToken);
String kid = signedJWT.getHeader().getKeyID();
JWK jwk = appleJwtKeyService.getPublicKey(kid);
boolean verified = signedJWT.verify(new ECDSAVerifier(jwk.toECKey()));
  • ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ฐ•ํ™”: ํ† ํฐ ๊ฒ€์ฆ ์‹คํŒจ, ํ‚ค ๋ฏธ์กด์žฌ ๋“ฑ ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ ์ƒํ™ฉ์— ๋Œ€๋น„ํ•œ ์„ธ๋ถ„ํ™”๋œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์„ฑ๊ณผ:

  • Apple ์„œ๋ฒ„ ํ˜ธ์ถœ์„ ์ตœ์†Œํ™”ํ•˜์—ฌ API ์ œํ•œ์— ๊ฑธ๋ฆด ์œ„ํ—˜์„ ๋‚ฎ์ถ”์—ˆ์Šต๋‹ˆ๋‹ค.
  • ํ† ํฐ ๊ฒ€์ฆ ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œ์ผœ ๋กœ๊ทธ์ธ ์‘๋‹ต ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ‚ค ๊ด€๋ฆฌ์˜ ๋ณต์žก์„ฑ์„ ์บก์Аํ™”ํ•˜์—ฌ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํ–ฅ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

2. JWT ํ† ํฐ ๋ณด์•ˆ ๊ฐ•ํ™” ๊ณผ์ œ

๋ฌธ์ œ ์ƒํ™ฉ:

  • JWT๋Š” ๋ฐœ๊ธ‰ ํ›„ ๋งŒ๋ฃŒ๋˜๊ธฐ ์ „๊นŒ์ง€ ์œ ํšจํ•˜์—ฌ, ํ† ํฐ์ด ํƒˆ์ทจ๋  ๊ฒฝ์šฐ ๋ณด์•ˆ ์œ„ํ—˜์ด ํฌ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
  • Access Token์˜ ์œ ํšจ ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ์„ค์ •ํ•˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ์ €ํ•˜๋˜๊ณ , ๊ธธ๊ฒŒ ์„ค์ •ํ•˜๋ฉด ๋ณด์•ˆ ์œ„ํ—˜์ด ์ฆ๊ฐ€ํ•˜๋Š” ๋”œ๋ ˆ๋งˆ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • Refresh Token์˜ ์žฅ๊ธฐ ๋ณด๊ด€์— ๋”ฐ๋ฅธ ๋ณด์•ˆ ์œ„ํ—˜๋„ ๊ณ ๋ คํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • Refresh Token Rotation ๊ตฌํ˜„: ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ณ , ์‚ฌ์šฉ ์‹œ ์ƒˆ๋กœ์šด ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•˜๋Š” ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
// RefreshTokenService.java์˜ ์ผ๋ถ€๋ถ„
@Transactional
public RefreshTokenResponse refreshAccessToken(String token) {
    // ํ† ํฐ ๊ฒ€์ฆ
    RefreshToken refreshToken = refreshTokenRepository.findByToken(token)
            .orElseThrow(() -> new RefreshTokenNotFoundException("์กด์žฌํ•˜์ง€ ์•Š๋Š” Refresh Token ์ž…๋‹ˆ๋‹ค."));

    // ์‚ฌ์šฉ ์—ฌ๋ถ€ ํ™•์ธ (์žฌ์‚ฌ์šฉ ๊ฐ์ง€)
    if (refreshToken.isUsed()) {
        // ๋ณด์•ˆ ์นจํ•ด ๊ฐ€๋Šฅ์„ฑ : ๋ชจ๋“  ํ† ํฐ ๋ฌดํšจํ™”
        refreshTokenRepository.deleteByUserId(refreshToken.getUserId());
        throw new TokenReuseDetectedException("ํ† ํฐ ์žฌ์‚ฌ์šฉ์ด ๊ฐ์ง€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ณด์•ˆ์„ ์œ„ํ•ด ์žฌ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.");
    }

    // ๊ธฐ์กด ํ† ํฐ ์‚ฌ์šฉ ์ฒ˜๋ฆฌ
    refreshToken.setUsed(true);
    refreshTokenRepository.save(refreshToken);

    // ์ƒˆ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์ƒ์„ฑ (Rotation)
    RefreshToken newRefreshToken = createRefreshToken(refreshToken.getUserId());
    // ์ƒˆ ์•ก์„ธ์Šค ํ† ํฐ ์ƒ์„ฑ...
}
  • ํ† ํฐ ์žฌ์‚ฌ์šฉ ๊ฐ์ง€ ๋ฉ”์ปค๋‹ˆ์ฆ˜: ์ด๋ฏธ ์‚ฌ์šฉ๋œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์ด ๋‹ค์‹œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ, ํ† ํฐ ํƒˆ์ทจ ์ƒํ™ฉ์œผ๋กœ ํŒ๋‹จํ•˜๊ณ  ํ•ด๋‹น ์‚ฌ์šฉ์ž์˜ ๋ชจ๋“  ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋ฌดํšจํ™”ํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. Access Token๊ณผ Refresh Token์˜ ์œ ํšจ ๊ธฐ๊ฐ„ ์ตœ์ ํ™”: Access Token์€ 30๋ถ„์œผ๋กœ ์งง๊ฒŒ, Refresh Token์€ 30์ผ๋กœ ์„ค์ •ํ•˜์—ฌ ๋ณด์•ˆ๊ณผ ์‚ฌ์šฉ์„ฑ์˜ ๊ท ํ˜•์„ ๋งž์ถ”์—ˆ์Šต๋‹ˆ๋‹ค.
// application-jwt.yml
jwt:
  access-token-validity-in-seconds: 1800  # 30๋ถ„
  refresh-token-validity-in-seconds: 2592000  # 30์ผ

์„ฑ๊ณผ:

  • ํ† ํฐ ํƒˆ์ทจ ์ƒํ™ฉ์—์„œ๋„ ๊ณต๊ฒฉ์ž๊ฐ€ ํ† ํฐ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ฒŒ ํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ† ํฐ ์žฌ์‚ฌ์šฉ ๊ฐ์ง€ ์‹œ ์ฆ‰๊ฐ์ ์ธ ๋Œ€์‘์„ ํ†ตํ•ด ์ถ”๊ฐ€ ํ”ผํ•ด๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • Access Token์˜ ์งง์€ ์œ ํšจ ๊ธฐ๊ฐ„์œผ๋กœ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๋ฉด์„œ๋„, Refresh Token Rotation์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ•ด์น˜์ง€ ์•Š๋Š” ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

3. ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ๋„์ „

๋ฌธ์ œ ์ƒํ™ฉ:

  • ์—ฌ๋Ÿฌ ์†Œ์…œ ๋กœ๊ทธ์ธ ์ œ๊ณต์ž(Google, Apple, Kakao)์˜ ์ƒ์ดํ•œ ์ธ์ฆ ๋ฐฉ์‹์„ ์ผ๊ด€๋˜๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒˆ๋กœ์šด ์†Œ์…œ ๋กœ๊ทธ์ธ์ด ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜ ๊ธฐ์กด API๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ „์ฒด ์‹œ์Šคํ…œ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š” ๊ตฌ์กฐ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ ์†Œ์…œ ๋กœ๊ทธ์ธ์˜ ์„ธ๋ถ€ ๊ตฌํ˜„์€ ๋‹ค๋ฅด์ง€๋งŒ, ๋ฐฑ์—”๋“œ์—์„œ๋Š” ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ธ์ฆ ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • ์ „๋žต ํŒจํ„ด๊ณผ ํŒฉํ† ๋ฆฌ ํŒจํ„ด ๊ฒฐํ•ฉ: SocialLoginService ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•˜๊ณ  ๊ฐ ์†Œ์…œ ๋กœ๊ทธ์ธ ์ œ๊ณต์ž๋ณ„๋กœ ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฉฐ, SocialLoginFactory๋ฅผ ํ†ตํ•ด ์ ์ ˆํ•œ ๊ตฌํ˜„์ฒด๋ฅผ ์„ ํƒํ•˜๋Š” ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
// SocialLoginService.java
public interface SocialLoginService {
    LoginResponse login(String idToken);
    SocialProvider getSocialProvider();
}

// SocialLoginFactory.java์˜ ์ผ๋ถ€๋ถ„
@Component
@RequiredArgsConstructor
public class SocialLoginFactory {
    private final Set<SocialLoginService> loginServices;

    public SocialLoginService getLoginService(SocialProvider socialProvider) {
        return loginServices.stream()
                .filter(service -> service.getSocialProvider() == socialProvider)
                .findFirst()
                .orElseThrow(() -> new UnsupportedSocialTypeException(
                        "์ง€์›ํ•˜์ง€ ์•Š๋Š” ์†Œ์…œ ๋กœ๊ทธ์ธ์ž…๋‹ˆ๋‹ค: " + socialProvider));
    }
}
  • ์˜์กด์„ฑ ์ฃผ์ž…๊ณผ ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ํ™œ์šฉ: Spring์˜ ์˜์กด์„ฑ ์ฃผ์ž… ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด ์†Œ์…œ ๋กœ๊ทธ์ธ ์„œ๋น„์Šค๋ฅผ ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ํ•„์š”ํ•œ ๊ณณ์—์„œ ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ถ”์ƒํ™” ๊ณ„์ธต ๋„์ž…: ์†Œ์…œ ๋กœ๊ทธ์ธ์˜ ์„ธ๋ถ€ ๊ตฌํ˜„ ์ฐจ์ด๋ฅผ ์ถ”์ƒํ™”ํ•˜์—ฌ ์ปจํŠธ๋กค๋Ÿฌ ๊ณ„์ธต์—์„œ๋Š” ๋‹จ์ผ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
// AuthRestController.java์˜ ์ผ๋ถ€๋ถ„
@PostMapping("/login")
public ResponseEntity<ApiResult<LoginResponse>> socialLogin(@RequestBody SocialLoginRequest request) {
    SocialLoginService loginService = socialLoginFactory.getLoginService(request.getProvider());
    LoginResponse response = loginService.login(request.getIdToken());
    return ResponseEntity.ok(ApiResult.success(response));
}

์„ฑ๊ณผ:

  • ์ƒˆ๋กœ์šด ์†Œ์…œ ๋กœ๊ทธ์ธ ์ œ๊ณต์ž ์ถ”๊ฐ€ ์‹œ ๊ธฐ์กด ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด ์ƒˆ๋กœ์šด ๊ตฌํ˜„์ฒด๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋˜๋Š” ํ™•์žฅ์„ฑ์„ ํ™•๋ณดํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฐ ์†Œ์…œ ๋กœ๊ทธ์ธ์˜ API ๋ณ€๊ฒฝ ์‹œ ํ•ด๋‹น ๊ตฌํ˜„์ฒด๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋˜์–ด ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์ปจํŠธ๋กค๋Ÿฌ์™€ ์„œ๋น„์Šค ๊ณ„์ธต์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”์–ด ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.
  • Spring์˜ ๊ธฐ๋Šฅ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜์—ฌ ๊ตฌํ˜„ ๋ณต์žก๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.

4. ๋ชจ๋ฐ”์ผ ์•ฑ์„ ์œ„ํ•œ ์ธ์ฆ ํ๋ฆ„ ์ตœ์ ํ™”

๋ฌธ์ œ ์ƒํ™ฉ:

  • ๋ชจ๋ฐ”์ผ ์•ฑ์—์„œ๋Š” ํ† ํฐ ๊ธฐ๋ฐ˜ ์ธ์ฆ ์‹œ ๋‹ค์–‘ํ•œ ์ƒํƒœ(ํ† ํฐ ์œ ํšจ, ๋งŒ๋ฃŒ, ์žฌ๋ฐœ๊ธ‰ ๋“ฑ)๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์•ฑ ๊ฐœ๋ฐœ์ž์™€ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž ๊ฐ„ API ์‘๋‹ต ํ˜•์‹์— ๋Œ€ํ•œ ์ดํ•ด ์ฐจ์ด๋กœ ํ†ตํ•ฉ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ณด์•ˆ๊ณผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์‚ฌ์ด์˜ ๊ท ํ˜•์„ ๋งž์ถ”๋Š” ํ† ํฐ ๊ด€๋ฆฌ ์ „๋žต์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  • ์ธ์ฆ API ์‘๋‹ต ํ†ต์ผ: ๋ชจ๋“  ์ธ์ฆ ๊ด€๋ จ API๊ฐ€ ์ผ๊ด€๋œ LoginResponse ํ˜•์‹์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.
// AuthRestController.java์˜ ์ผ๋ถ€๋ถ„
@GetMapping("/login")
public ResponseEntity<ApiResult<LoginResponse>> validateToken(@AuthenticationPrincipal UserPrincipal principal) {
    LoginResponse loginResponse = authService.validateAndRefreshToken(principal.getId());
    return ResponseEntity.ok(ApiResult.success(loginResponse));
}
  • AuthService ๋„์ž…: ์ธ์ฆ ๊ด€๋ จ ๋กœ์ง์„ ์ค‘์•™ํ™”ํ•˜์—ฌ ์ฝ”๋“œ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ณ  ์ผ๊ด€๋œ ์ธ์ฆ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • API ๋ฌธ์„œํ™”(Markdown(Mermaid)): ๊ฐ ์ธ์ฆ API์˜ ๋ชฉ์ ๊ณผ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋ช…ํ™•ํžˆ ์„ค๋ช…ํ•˜์—ฌ ์•ฑ ๊ฐœ๋ฐœ์ž์˜ ์ดํ•ด๋ฅผ ๋•๋Š” ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

์„ฑ๊ณผ:

  • ์•ฑ๊ณผ ๋ฐฑ์—”๋“œ ๊ฐ„ ์›ํ™œํ•œ ํ†ตํ•ฉ์œผ๋กœ ๊ฐœ๋ฐœ ์†๋„๊ฐ€ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž์—๊ฒŒ ํˆฌ๋ช…ํ•œ ์ธ์ฆ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋ฉด์„œ๋„ ๋ณด์•ˆ์„ ์œ ์ง€ํ•˜๋Š” ๊ท ํ˜•์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค์–‘ํ•œ ์ธ์ฆ ์ƒํƒœ์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ์ฒ˜๋ฆฌ ํ๋ฆ„์„ ๊ตฌ์ถ•ํ•˜์—ฌ ์•ฑ์˜ ์•ˆ์ •์„ฑ์ด ํ–ฅ์ƒ๋  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ธฐ์ˆ ์  ๋„์ „๋“ค์„ ํ•ด๊ฒฐํ•˜๋ฉด์„œ, ๋ณด์•ˆ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ๋ชจ๋‘ ๊ณ ๋ คํ•œ ๊ฒฌ๊ณ ํ•œ ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๊ณ ์ž ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋ชจ๋ฐ”์ผ ์•ฑ ํŠน์„ฑ์— ๋งž๋Š” ์ธ์ฆ ์‹œ์Šคํ…œ ๊ตฌํ˜„๊ณผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ•ด์น˜์ง€ ์•Š๋Š” ๋ณด์•ˆ ๊ฐ•ํ™” ๋ฐฉ์•ˆ์„ ๋ชจ์ƒ‰ํ•˜๋Š” ๊ณผ์ •์ด ๊ฐ€์žฅ ํฐ ํ•™์Šต ํฌ์ธํŠธ์˜€์Šต๋‹ˆ๋‹ค.

ํ–ฅํ›„ ๊ฐœ๋ฐœ ๊ณ„ํš๊ณผ ์ด์œ 

FCM ํ‘ธ์‹œ ์•Œ๋ฆผ ๊ตฌํ˜„

  • ์„œ๋น„์Šค์˜ ํ•ต์‹ฌ ๊ฐ€์น˜์ธ "์ตœ์ €๊ฐ€ ์•Œ๋ฆผ"์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ํ•„์ˆ˜์ ์œผ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ‘ธ์‹œ ์•Œ๋ฆผ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๋Š” ์•ฑ์„ ์‹คํ–‰ํ•˜์ง€ ์•Š์•„๋„ ์ค‘์š” ์ •๋ณด๋ฅผ ๋ฐ›์•„๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages