๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

TIL, WIL/WIL๐Ÿ“—

4์›” ๋‘˜์งธ ์ฃผ : Docker ๊ฐ€์ƒํ™” + Refresh Token with Redis

728x90

๋„์ž… ์ทจ์ง€

- ๊ทธ ๋™์•ˆ์˜ ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜์„ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ์–ธ์–ด, ๊ทธ ์™ธ ์‹คํ–‰ ํ™˜๊ฒฝ ๋“ฑ์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•˜๋ฉฐ ๊ฐ ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ํ•„์š” ํ™˜๊ฒฝ์ด ๋‹ค๋ฅด๊ณ  ๊ฐœ๋ฐœ์ž์˜ ๋กœ์ปฌ ๋จธ์‹  ํ™˜๊ฒฝ ๋˜ํ•œ ๋งค์šฐ ๋‹ค์–‘ํ•˜๋ฏ€๋กœ, ์ด ๊ฒฝ์šฐ ๋„์ปค๋ฅผ ํ™œ์šฉํ•œ๋‹ค๋ฉด ํ™˜๊ฒฝ ์กฐ์„ฑ์„ ๋งค์šฐ ํšจ์œจ์ ์œผ๋กœ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด ๋„์ž…ํ•ด๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค.

-  JWT ํ† ํฐ์„ ํ™œ์šฉํ•œ ๋กœ๊ทธ์ธ ๋ฐฉ์‹์— ๋ณด์•ˆ์„ ๋” ๊ฐ•ํ™”ํ•œ ๋ฐฉ์‹์„ ๋„์ž…ํ•ด๋ณด๊ณ ์ž, ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์— ๋Œ€ํ•ด ๋ฐฐ์šฐ๊ณ  ๋„์ž…ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.

์žฅ์ 

- ๋กœ์ปฌ์—์„œ ์–ธ์–ด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“ฑ์„ ๋”ฐ๋กœ ์„ค์น˜ํ•˜์ง€ ์•Š์•„๋„ ๋„์ปค ๋ช…๋ น์„ ํ†ตํ•ด ํ˜ธ์ŠคํŠธ ๋จธ์‹ ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

- ์›๊ฒฉ ๋ฆฌ๋ชจํŠธ ๋จธ์‹ ์— ๋„์ปคํ™”ํ•œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฐฐํฌํ•ด ํšจ์œจ์ ์œผ๋กœ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

- ์•ก์„ธ์Šค ํ† ํฐ์˜ ์œ ํšจ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ์„ค์ •ํ•ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๋ฉด์„œ๋„, ์‚ฌ์šฉ์ž์˜ ๋กœ๊ทธ์ธ ์ฃผ๊ธฐ๋ฅผ ๊ธธ๊ฒŒ ์œ ์ง€์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

๊ตฌํ˜„ ๋ชฉํ‘œ

- JWT Refresh Token์„ ์ €์žฅํ•˜๋Š” Redis ๊ตฌํ˜„ ๋งŸ ํ”„๋กœ์ ํŠธ ์ด๋ฏธ์ง€๋ฅผ ๋„์ปค ํ—ˆ๋ธŒ์— ํ‘ธ์‹œ

- AWS EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฆฌ๋ชจํŠธ ๋จธ์‹ ์œผ๋กœ ํ™œ์šฉํ•ด ํ”„๋กœ์ ํŠธ ๋ฐฐํฌ

๊ตฌํ˜„ ์ „ ์˜ˆ์ƒ๋˜๋Š” ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

- Node.js๋กœ ํ•™์Šต ๋ฐ ์‹ค์Šตํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Java ๋ฐ ์Šคํ”„๋ง, ์Šคํ”„๋ง ๋ถ€ํŠธ์— ๋„์ปค๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ์€ ์‹œํ–‰์ฐฉ์˜ค๊ฐ€ ์˜ˆ์ƒ๋œ๋‹ค.

- ๋ ˆ๋””์Šค๋กœ ๋ฆฌํ”„๋ ˆ์‰ฌ ํ† ํฐ์„ ๊ตฌํ˜„ํ•  ๋•Œ์˜ ์‹œํ–‰์ฐฉ์˜ค๊ฐ€ ์˜ˆ์ƒ๋œ๋‹ค.

์‹คํ–‰ ํ™˜๊ฒฝ

- Java 17

- Springboot 2.7.6

- Gradle

- Redis 2.7.6

1. Redis๋กœ Refresh ํ† ํฐ ๊ตฌํ˜„

- ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๊ตฌํ˜„์— ๋ ˆ๋””์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ  : ๋ ˆ๋””์Šค๋Š” ๋ฐ์ดํ„ฐ๋ฅผ RAM์— ์ €์žฅํ•˜๋ฏ€๋กœ ์˜์†์„ฑ์ด ์—†๋‹ค. ๋Œ€์‹  ๋ฐ์ดํ„ฐ ์—‘์„ธ์Šค ์†๋„๊ฐ€ ๋งค์šฐ ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ๋กœ ์บ์‹ฑํ•  ๋•Œ ์‚ฌ์šฉํ•˜๊ณ , ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์˜ ๊ฒฝ์šฐ ์˜์†์„ฑ์ด ํ•„์š”์—†๊ณ , ๋น ๋ฅธ ์—‘์„ธ์Šค ์†๋„๊ฐ€ ์ค‘์š”ํ•˜๋ฏ€๋กœ ์„ ํƒํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ๋˜ํ•œ ๋ ˆ๋””์Šค์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ์˜ ์œ ํšจ ๊ธฐ๊ฐ„์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด, ์ผ์ • ๊ธฐ๊ฐ„ ํ›„ ๋งŒ๋ฃŒ๋˜์–ด์•ผ ํ•˜๋Š” ํ† ํฐ์˜ ๊ฒฝ์šฐ์— ์ ํ•ฉํ•˜๋‹ค.

 

Refresh ํ† ํฐ ์›๋ฆฌ

- ๋กœ๊ทธ์ธ ์‹œ ์—‘์„ธ์Šค ํ† ํฐ๊ณผ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ JWT ํ† ํฐ์œผ๋กœ ๋ฐœ๊ธ‰๋ฐ›๊ณ , ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ๋งŒ ์„œ๋ฒ„์˜ DB์— ์ €์žฅํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์•ก์„ธ์Šค ํ† ํฐ๊ณผ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋ชจ๋‘ ๋ฐœ๊ธ‰ํ•œ๋‹ค.

- ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋น„์Šค๋ฅผ ์ด์šฉํ•  ๋•Œ๋งˆ๋‹ค ์š”์ฒญ์— ์•ก์„ธ์Šค & ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋‹ด์•„ ๋ณด๋‚ด๊ณ , ์•ก์„ธ์Šค ํ† ํฐ์˜ ์œ ํšจ๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜์—ˆ์„ ๋•Œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์ด ์œ ํšจํ•˜๋‹ค๋ฉด ์„œ๋ฒ„์—์„œ ์•ก์„ธ์Šค ํ† ํฐ์„ ์žฌ๋ฐœ๊ธ‰ํ•œ๋‹ค.

 

์•„์ง ๋„๋ฉ”์ธ๋ณ„๋กœ ๋‚˜๋ˆˆ ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ์–ด์„œ, ๊ฐ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์„œ๋กœ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ ํ›„ ๋‹ค์‹œ ์—…๋ฐ์ดํŠธ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

 

1) ๋กœ๊ทธ์ธ

- JWT ํ† ํฐ ๋ฐœ๊ธ‰ : ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ํ• ๋•Œ, access token์€ ๋ฐœ๊ธ‰ํ›„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์‘๋‹ต ํ—ค๋”์— ์ถ”๊ฐ€๋œ๋‹ค.

@Component
@RequiredArgsConstructor
public class JwtUtil {

  private static final String BEARER_PREFIX = "Bearer ";
  
  private static final long ACCESS_TOKEN_TIME = (long) 60 * 60;
  
  @Value("${jwt.secret.key}")
  private String secretKey;
  
  private Key key;
  
  private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
  
  @PostConstruct
  public void init() {
    byte[] bytes = Base64.getDecoder().decode(secretKey);
    key = Keys.hmacShaKeyFor(bytes);
  }

  public String createAccessToken(String username) {
    Date date = new Date();

    return BEARER_PREFIX +
        Jwts.builder()
            .setSubject(username)
            .setExpiration(new Date(date.getTime() + ACCESS_TOKEN_TIME))
            .setIssuedAt(date)
            .signWith(key, signatureAlgorithm)
            .compact();
  }
}

- refresh token์€ ์ž„์˜์˜ ํ† ํฐ ํ˜•์‹์œผ๋กœ ๋ฐœ๊ธ‰ ํ›„ ์„œ๋ฒ„์— ์ €์žฅ๋œ๋‹ค. ๋˜ํ•œ HttpOnly ์ฟ ํ‚ค์— ๋‹ด๊ฒจ ์‘๋‹ต ํ—ค๋”์— ์ถ”๊ฐ€๋œ๋‹ค.

@Service
@RequiredArgsConstructor
public class RefreshTokenService {

  private final UserRepository userRepository;

  private final UserService userService;

  private final RefreshTokenRepository refreshTokenRepository;

  public String createRefreshToken(String username) {
    RefreshToken refreshToken = new RefreshToken(UUID.randomUUID().toString(),
        findByUsername(username).getUsername());
    saveRefreshToken(refreshToken);
    return refreshToken.toString();
  }

  public Cookie createRefreshCookie(String stringRefreshToken) {
    Cookie cookie = new Cookie("refreshToken", stringRefreshToken);
    cookie.setMaxAge(60 * 60 * 336);
    cookie.setHttpOnly(true);
    cookie.setSecure(true);
    return cookie;
  }

  private void saveRefreshToken(RefreshToken refreshToken) {
    refreshTokenRepository.save(refreshToken);
  }

  private User findByUsername(String username) {
    return userRepository.findByUsername(username)
        .orElseThrow(() -> new IllegalArgumentException("ํ•ด๋‹น ์œ ์ €๋Š” ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค"));
  }
}

- ๋ฐœ๊ธ‰๋œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์€ ์„œ๋ฒ„์— ์ €์žฅ๋œ๋‹ค.

public interface RefreshTokenRepository extends CrudRepository<RefreshToken, String> {

}

- login ์ปจํŠธ๋กค๋Ÿฌ

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/user")
public class UserController {

  private final UserService userService;

  @PostMapping("/login")
  public ResponseEntity<MessageResponseDto> login(@RequestBody LoginRequestDto loginRequestDto,
      HttpServletResponse response) {
    userService.login(loginRequestDto, response);
    return ResponseEntity.status(HttpStatus.OK).build();
  }
}

- UserService์˜ Login ๋ฉ”์„œ๋“œ

@Service
@RequiredArgsConstructor
public class UserService {

  private final UserRepository userRepository;

  private final JwtUtil jwtUtil;

  private final RefreshTokenService refreshTokenService;

  @Transactional
  public void login(LoginRequestDto loginRequestDto, HttpServletResponse response) {
    User user = findByUsername(loginRequestDto.getUsername());

    // ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ
    if (!user.getPassword().equals(loginRequestDto.getPassword())) {
      throw new IllegalArgumentException("๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
    }

    // token ๋ฐœ๊ธ‰
    response.addHeader(JwtUtil.AUTHORIZATION_HEADER, jwtUtil.createAccessToken(user.getUsername()));

    response.addCookie(
        refreshTokenService.createRefreshCookie(
            refreshTokenService.createRefreshToken(user.getUsername())));
  }

  private User findByUsername(String username) {
    return userRepository.findByUsername(username)
        .orElseThrow(() -> new IllegalArgumentException("ํ•ด๋‹น ์œ ์ €๋Š” ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค"));
  }
}

- Redis๋Š” ํ‚ค : ๊ฐ’ ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์•„์ด๋””๋„ ๋ฌธ์ž์—ด์ด๋‹ค.

@RedisHash(value = "refreshToken", timeToLive = 60 * 60 * 336L)
@Getter
public class RefreshToken {

  @Id
  private String refreshToken;

  private String username;

  public RefreshToken(String refreshToken, String username) {
    this.refreshToken = refreshToken;
    this.username = username;
  }

2) JWT ํ† ํฐ ๊ฒ€์ฆ ๋ฐ ์žฌ๋ฐœ๊ธ‰

- ์š”์ฒญ ์‹œ ๋งˆ๋‹ค Spring Security ์ธ์ฆ/์ธ๊ฐ€ ์ง„ํ–‰

@Slf4j // ๋กœ๊น…์— ๋Œ€ํ•ด ์ถ”์ƒ ๋ ˆ์ด์–ด๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {

  private final JwtUtil jwtUtil;

 @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {
    String resolveAccessToken = jwtUtil.resolveAccessToken(request);
    String refreshToken = separateRefreshToken(resolveRefreshTokenFromCookies(request))[0];
    String username = separateRefreshToken(resolveRefreshTokenFromCookies(request))[1];

    if (resolveAccessToken != null) {
      if (!jwtUtil.validateAccessToken(resolveAccessToken) && jwtUtil.checkExpirationToken(
          resolveAccessToken, refreshToken, username, response)) {
        jwtExceptionHandler(response, "Token Error", HttpStatus.UNAUTHORIZED.value());
      }
      setAuthentication(jwtUtil.getUserInfoFromToken(resolveAccessToken).getSubject());
    }
    filterChain.doFilter(request, response);
  }

  private void setAuthentication(String username) { // ์ธ์ฆ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ๋“ฑ๋ก : ์ฝ”๋“œ ๊ฐ„์†Œํ™” ๋ฐ ์ฑ…์ž„ ๋ถ„๋ฆฌ ๋ชฉ์ 
    SecurityContext context = SecurityContextHolder.createEmptyContext(); // ContextHolder์— ์ธ์ฆ ๊ฐ์ฒด ์ €์žฅ
    Authentication authentication = jwtUtil.createAuthentication(username);
    context.setAuthentication(authentication);

    SecurityContextHolder.setContext(context);
  }

  // ์ฟ ํ‚ค์—์„œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์ถ”์ถœ
  public String resolveRefreshTokenFromCookies(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();
    String resolveRefreshToken = null;
    for (Cookie cookie : cookies) {
      if (cookie.getName().equals("refreshToken")) {
        resolveRefreshToken = cookie.getValue();
      }
    }
    return resolveRefreshToken;
  }

  // ํ† ํฐ๊ณผ username ๋ถ„๋ฆฌ
  public String[] separateRefreshToken(String cookieValue) {
    return cookieValue.split(":");
  }

  // ํ† ํฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, Exception ๊ฒฐ๊ณผ๊ฐ’์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค
  private void jwtExceptionHandler(HttpServletResponse response, String msg, int statusCode) {
    response.setStatus(statusCode);
    response.setContentType("application/json");
    try {
      // ObjectMapper๋ฅผ ํ†ตํ•ด ๋ณ€ํ™˜ํ•œ๋‹ค
      String json = new ObjectMapper().writeValueAsString(new MessageResponseDto(msg, statusCode));
      response.getWriter().write(json);
    } catch (Exception e) {
      log.error(e.getMessage());
    }
  }
}

0) ์—‘์„ธ์Šค ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์—ˆ๋‹ค๋ฉด

1) ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ํ™•์ธ, ๋งŒ๋ฃŒ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋ฉด ์•ก์„ธ์Šค ํ† ํฐ์„ ์žฌ๋ฐœ๊ธ‰ํ•˜๊ณ  ์ด๋ฏธ ๋งŒ๋ฃŒ๋˜์—ˆ๋‹ค๋ฉด ๋กœ๊ทธ์•„์›ƒํ•œ๋‹ค.

2) ๋กœ๊ทธ์•„์›ƒ ์‹œ ์•ก์„ธ์Šค ํ† ํฐ๊ณผ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์€ ์‚ญ์ œ๋œ๋‹ค.

- refreshService

@Service
@RequiredArgsConstructor
public class RefreshTokenService {

  private final RefreshTokenRepository refreshTokenRepository;

  private static final String BEARER_PREFIX = "Bearer ";

  private static final long ACCESS_TOKEN_TIME = (long) 60 * 60;

  @Value("${jwt.secret.key}")
  private String secretKey;

  private Key key;

  private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

  @PostConstruct
  private void init() {
    byte[] bytes = Base64.getDecoder().decode(secretKey);
    key = Keys.hmacShaKeyFor(bytes);
  }

  // ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๊ฒ€์ฆ ๋ฐ ๋กœ๊ทธ์•„์›ƒ ์—ฐ๊ฒฐ
  public void checkRefreshToken(String StringRefreshToken, String username,
      HttpServletResponse response) {
    if (refreshTokenRepository.existsById(StringRefreshToken)) {
      regenerateRefreshToken(username, response);

    } else {
      RefreshToken refreshToken = refreshTokenRepository.findById(StringRefreshToken).orElseThrow(
          () -> new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ† ํฐ์ž…๋‹ˆ๋‹ค.")
      );
      deleteRefreshToken(refreshToken);
    }
  }

  public void deleteRefreshToken(RefreshToken refreshToken) {
    refreshTokenRepository.delete(refreshToken);
  }

  private void regenerateRefreshToken(String username, HttpServletResponse response) {
    Date date = new Date();

    String newAccessToken = BEARER_PREFIX +
        Jwts.builder()
            .setSubject(username)
            .setExpiration(new Date(date.getTime() + ACCESS_TOKEN_TIME))
            .setIssuedAt(date)
            .signWith(key, signatureAlgorithm)
            .compact();

    response.addHeader(JwtUtil.AUTHORIZATION_HEADER, newAccessToken);
  }

  public RefreshToken findById(String refreshToken) {
    return refreshTokenRepository.findById(refreshToken).orElseThrow(
        () -> new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ† ํฐ์ž…๋‹ˆ๋‹ค.")
    );
  }
}

 

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/user")
public class UserController {

  private final UserService userService;

  @PostMapping("/logout")
  public ResponseEntity<MessageResponseDto> logout(HttpServletRequest request,
      HttpServletResponse response) {
    userService.logout(request, response);
    return ResponseEntity.status(HttpStatus.OK).build();
  }

}

- userService

@Service
@RequiredArgsConstructor
public class UserService {

  private final UserRepository userRepository;

  private final JwtAuthFilter jwtAuthFilter;

  private final RefreshTokenService refreshTokenService;

  public void logout(HttpServletRequest request, HttpServletResponse response) {
    response.setHeader(JwtUtil.AUTHORIZATION_HEADER, null);
    refreshTokenService.deleteRefreshToken(refreshTokenService.findById(
        jwtAuthFilter.separateRefreshToken(
            jwtAuthFilter.resolveRefreshTokenFromCookies(request))[0]));
  }

  private User findByUsername(String username) {
    return userRepository.findByUsername(username)
        .orElseThrow(() -> new IllegalArgumentException("ํ•ด๋‹น ์œ ์ €๋Š” ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค"));
  }
}

2. ํ”„๋กœ์ ํŠธ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ Dockerfile ์ž‘์„ฑ

- ๋„์ปคํŒŒ์ผ์„ ์ž‘์„ฑํ•ด ๋„์ปค๊ฐ€ ๋ช…๋ น์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

1) ํ”„๋กœ์ ํŠธ Dockerize

- Gradle์˜ bootjar๋ฅผ ์ด์šฉํ•ด ์Šค๋ƒ…์ƒท์„ ๋งŒ๋“ ๋‹ค

- Dockfile ์ƒ์„ฑ

FROM openjdk:17-alpine

VOLUME /tmp

ARG JAR_FILE

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

2) Docker-Compose.yaml

version: "2.17.2"
services:
  redis:
    image: 'redis'
  backend:
    build:
      context: .
      args:
        JAR_FIlE: build/libs/*.jar
    image: backend
    depends_on:
      - redis

3. EC2์ธ์Šคํ„ด์Šค๋ฅผ ์›๊ฒฉ ๋ฆฌ๋ชจํŠธ ๋จธ์‹ ์œผ๋กœ ํ•˜์—ฌ ๋ฐฐํฌํ•˜๊ธฐ

- ๊ฐ•์˜์—์„œ ๋ฐฐ์šด ECS ์„œ๋น„์Šค๋Š” ์‹ค์ œ๋กœ ๊ฒฐ์ œ๊ฐ€ ๋˜๊ณ , ๊ทธ ์„œ๋น„์Šค๊นŒ์ง€ ํ•„์š”ํ•˜๋‹ค๊ณ ๋Š” ์ƒ๊ฐํ•˜์ง€ ์•Š์•„ ์ˆ˜๋™ ๋ฐฐํฌ ๋ฐฉ์‹์„ ์„ ํƒํ•˜๊ณ  ๋„์ž…ํ•ด๋ณด์•˜๋‹ค.

https://cdaosldk.tistory.com/242

 

Udemy : Docker & Kubernetes ์‹ค์ „ ๊ฐ€์ด๋“œ (9) - 1

Docker ์ปจํ…Œ์ด๋„ˆ ๋ฐฐํฌํ•˜๊ธฐ - ์ˆ˜๋™ ๋ฐฐํฌ 1. ๋ฐฐํฌ ํ”„๋กœ์„ธ์Šค & ํ”„๋กœ๋ฐ”์ด๋” 1) ๋ฆฌ๋ชจํŠธ ์„œ๋ฒ„ ์„ค์ • 2) SSH ์—ฐ๊ฒฐ 3) ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ ๋จธ์‹ ์—์„œ ๋„์ปค ์ด๋ฏธ์ง€ ํ‘ธ์‹œ 4) ๋ฆฌ๋ชจํŠธ ํ˜ธ์ŠคํŠธ์—์„œ ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ํ›„ ์›น์— ํฌํŠธ

cdaosldk.tistory.com

4. ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

- ๋ ˆ๋””์Šค๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋„์ค‘ ํ† ํฐ์„ ๊ฒ€์ฆํ•˜๊ณ  ์žฌ๋ฐœ๊ธ‰ํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋งŽ์ด ์†Œ์š”๋˜์—ˆ๋‹ค. ๊ฒ€์ƒ‰์„ ํ†ตํ•œ ํ•™์Šต ๋ฐ ์‹ค์ œ ์—ฌ๋Ÿฌ ์‹œํ–‰์ฐฉ์˜ค๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•ด๋ณธ ์ฝ”๋“œ์—ฌ์„œ, ํ’ˆ์งˆ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ๋งค์šฐ ๋ฟŒ๋“ฏํ•˜๋‹ค.

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

 

์ฐธ๊ณ ํ•œ ๊ธ€

https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-Access-Token-Refresh-Token-%EC%9B%90%EB%A6%AC-feat-JWT

https://medium.com/@uk960214/refresh-token-%EB%8F%84%EC%9E%85%EA%B8%B0-f12-dd79de9fb0f0

728x90