<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>작은일기장</title>
    <link>https://baam.tistory.com/</link>
    <description>나를 기록하는 공간</description>
    <language>ko</language>
    <pubDate>Fri, 10 Jul 2026 15:57:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>조용한외침</managingEditor>
    <item>
      <title>[Spring-boot] i18n적용 및 동적 데이터 변경</title>
      <link>https://baam.tistory.com/441</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 외국 컨퍼런스에 참여를 해야 하는 상황이 생겼습니다. 미리 준비해야 하는 상황이 생겼고, 어떤 식으로 구성해야 할지 고민을 많이 했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적인 데이터는 i18n을 활용하면 된다고 하지만 API요청에 따라 변하는 데이터(동적 데이터)는 어떤식으로 처리하면 좋을지 고민을 하게 됐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 어떤 식으로 정적, 동적 데이터를 처리했는지 글을 작성하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;br /&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;혹시라도 부정확한&amp;nbsp;정보를 전달드릴 수 있습니다. 다만 틀린 거나 부정확한 정보가 있다면 댓글을 남겨주세요.&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국어 / 영어 2개 언어를 지원하는 i18n 구현 과정을 단계별로 정리하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. src/main/resources 에 properties 파일을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- properties 파일은 한국어와 영어를 accept-language를 기반으로 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- properties 파일은 response message를 변환하기 위한 파일입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-04-14 오후 10.25.26.png&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I6YGT/dJMcabX9FcW/Kqf8DUVc4jMvLCxGech2g0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I6YGT/dJMcabX9FcW/Kqf8DUVc4jMvLCxGech2g0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I6YGT/dJMcabX9FcW/Kqf8DUVc4jMvLCxGech2g0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI6YGT%2FdJMcabX9FcW%2FKqf8DUVc4jMvLCxGech2g0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;264&quot; data-filename=&quot;스크린샷 2026-04-14 오후 10.25.26.png&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- properties의 예시입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1776174038482&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# messages.properties (한국어 - 기본)
# x로 표시된 것들은 직접 오류, response 등 관련 내용들을 작성하시면됩니다.
# Error Code
internal.x=서버 내부 오류가 발생했습니다.
invalid.x=잘못된 입력값입니다.
unauthorized=인증이 필요합니다.
member.x=멤버를 찾을 수 없습니다.

# Auth - Success
auth.x=회원가입이 완료되었습니다.
auth.x=로그인이 완료되었습니다.
auth.x=토큰이 재발급되었습니다.

# Auth - Validation
auth.x=사번은 필수입니다.
auth.x=사번은 100자를 초과할 수 없습니다.
auth.x=비밀번호는 필수입니다.

# messages_en.properties (영어)
# Error Code
internal.x=Internal server error occurred.
invalid.x=Invalid input value.
unauthorized=Unauthorized.
member.x=Member not found.

# Auth - Success
auth.x=Sign up has been completed.
auth.x=Login has been completed.
auth.x=Token has been reissued.

# Auth - Validation
auth.x=Employee number is required.
auth.x=Employee number cannot exceed 100 characters.
auth.x=Password is required.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저의 경우 파일 네이밍 규칙을 message_언어코드. properties로 설정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; ex. messages_ja.properties (일본어)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Application main에 'Accept-Language' 헤더 기반 Locale 코드 추가&lt;/p&gt;
&lt;pre id=&quot;code_1776175486028&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Bean으로 설정한 이유는 싱글톤으로 관리하고, 어플리케이션에서 하나의 인스턴스로 필요해서 입니다.

....
@Bean
public LocaleResolver localeResolver() {
	AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
	localeResolver.setDefaultLocale(Locale.KOREAN);
	return localeResolver;
}

@Bean
public ResourceBundleMessageSource messageSource() {
	ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setUseCodeAsDefaultMessage(true);
    messageSource.setDefaultEncoding(&quot;UTF-8&quot;);
    messageSource.setBasename(&quot;messages&quot;);
    return messageSource;
}
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AcceptHeaderLocaleResolver는 Accept-Language 헤더에서 전달받은 'ko', 'en'을 Locale 기반으로 변경하도록 구성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이후 Locale 기반으로 response, error code 등이 전달됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Error Code Enum 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저의 경우 Error Code를 Enum으로 관리하고 있는 상황에서 propeties에서 공통으로 전달하도록 수정했어야 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- HttpStatus, Error Code, Message Key(properties 파일의 키)&lt;/p&gt;
&lt;pre id=&quot;code_1776178144515&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@RequiredArgsConstructor
public enum ErrorCode {

    // Common
    INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, &quot;C001&quot;, &quot;internal.x&quot;),
    INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, &quot;C002&quot;, &quot;invalid.x&quot;),
    METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, &quot;C003&quot;, &quot;method.x&quot;),
    ACCESS_DENIED(HttpStatus.FORBIDDEN, &quot;C004&quot;, &quot;access.x&quot;),

    // Auth
    UNAUTHORIZED(HttpStatus.UNAUTHORIZED, &quot;A001&quot;, &quot;unauthorized&quot;),
    INVALID_CREDENTIALS(HttpStatus.UNAUTHORIZED, &quot;A002&quot;, &quot;invalid.x&quot;),
    TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, &quot;A003&quot;, &quot;token.x&quot;),
    .....

	private final HttpStatus httpStatus;
	private final String code;
	private final String message; // properties 파일의 키
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- message가 properties 파일에 있는 키로 사용하기 때문에 각 파일마다 동일한 키를 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. ApiResponse에 i18n 관련 코드 추가&lt;/p&gt;
&lt;pre id=&quot;code_1776179236463&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ApiResponse&amp;lt;T&amp;gt; {

    private boolean success;
    private String message;
    private T result;
    private LocalDateTime timestamp;

    /**
     * i18n 지원 성공 응답
     * - messageKey를 MessageSource에서 Locale에 맞는 메시지로 변환
     */
    public static &amp;lt;T&amp;gt; ApiResponse&amp;lt;T&amp;gt; success(String messageKey, T data,
                                              MessageSource messageSource, Locale locale) {
        String message = messageSource.getMessage(messageKey, null, messageKey, locale);
        return ApiResponse.&amp;lt;T&amp;gt;builder()
                .success(true)
                .message(message)
                .result(data)
                .timestamp(LocalDateTime.now())
                .build();
    }

    /** i18n 미사용 */
    public static &amp;lt;T&amp;gt; ApiResponse&amp;lt;T&amp;gt; success(T data) {
        return ApiResponse.&amp;lt;T&amp;gt;builder()
                .success(true)
                .message(&quot;Success&quot;)
                .result(data)
                .timestamp(LocalDateTime.now())
                .build();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저의 경우 messageSource.getMessage에서 properties파일로 전달하여 response를 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;1. 첫 번째 인자(messageKey) - 메시지 키&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;2. 두 번째 인자(null) - 메시지 파라미터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;3. 세 번째 인자(messageKey) - 키를 못 찾았을 때 반환할 기본값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;4. 네 번째 인자(locale) - 적용한 Locale&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. ErrorResponse에도 코드 추가&lt;/p&gt;
&lt;pre id=&quot;code_1776180143096&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ErrorResponse {

    private String code;
    private String message;
    private int status;
    private LocalDateTime timestamp;
    private List&amp;lt;FieldError&amp;gt; errors;

    /** i18n 지원 에러 응답 (비즈니스 예외) */
    public static ErrorResponse of(ErrorCode errorCode, MessageSource messageSource, Locale locale) {
        String message = messageSource.getMessage(
                errorCode.getMessage(),  // 메시지 키
                null,
                errorCode.getMessage(),  // 키 자체를 메시지로 사용
                locale
        );
        return ErrorResponse.builder()
                .code(errorCode.getCode())
                .message(message)
                .status(errorCode.getHttpStatus().value())
                .timestamp(LocalDateTime.now())
                .errors(new ArrayList&amp;lt;&amp;gt;())
                .build();
    }

    /** i18n 지원 에러 응답 (유효성 검증 에러) */
    public static ErrorResponse of(ErrorCode errorCode, BindingResult bindingResult,
                                    MessageSource messageSource, Locale locale) {
        String message = messageSource.getMessage(
                errorCode.getMessage(), null, errorCode.getMessage(), locale
        );
        return ErrorResponse.builder()
                .code(errorCode.getCode())
                .message(message)
                .status(errorCode.getHttpStatus().value())
                .timestamp(LocalDateTime.now())
                .errors(FieldError.of(bindingResult, messageSource, locale))
                .build();
    }

    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class FieldError {
        private String field;
        private String value;
        private String reason;

        /**
         * 유효성 검증 필드 에러 i18n 처리
         * - DTO의 @NotBlank(message = &quot;{key}&quot;) 형태의 메시지를 해석
         * - {key} 패턴이면 MessageSource에서 번역, 아니면 원본 사용
         */
        public static List&amp;lt;FieldError&amp;gt; of(BindingResult bindingResult,
                                           MessageSource messageSource, Locale locale) {
            List&amp;lt;FieldError&amp;gt; errors = new ArrayList&amp;lt;&amp;gt;();

            for (org.springframework.validation.FieldError error : bindingResult.getFieldErrors()) {
                String message = error.getDefaultMessage();

                // {key} 형태인 경우 MessageSource에서 번역된 메시지 조회
                if (message != null &amp;amp;&amp;amp; message.startsWith(&quot;{&quot;) &amp;amp;&amp;amp; message.endsWith(&quot;}&quot;)) {
                    String key = message.substring(1, message.length() - 1);
                    message = messageSource.getMessage(
                            key, error.getArguments(), error.getDefaultMessage(), locale
                    );
                }

                errors.add(new FieldError(
                        error.getField(),
                        error.getRejectedValue() == null ? &quot;&quot; : error.getRejectedValue().toString(),
                        message
                ));
            }
            return errors;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- DTO에서 Error를 전달할 때도 i18n의 키를 이용할 수 있도록 구성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. GlobalExceptionHandler에 Locale 주입&lt;/p&gt;
&lt;pre id=&quot;code_1776180275412&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    private final MessageSource messageSource;

    public GlobalExceptionHandler(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    /** 비즈니스 예외 처리 - i18n 메시지 사용 */
    @ExceptionHandler(BusinessException.class)
    protected ResponseEntity&amp;lt;ErrorResponse&amp;gt; handleBusinessException(
            BusinessException e,
            Locale locale  // Accept-Language 헤더에서 자동 해석된 Locale
    ) {
        log.warn(&quot;BusinessException: {}&quot;, e.getMessage(), e);
        ErrorCode errorCode = e.getErrorCode();
        ErrorResponse response = ErrorResponse.of(errorCode, messageSource, locale);
        return ResponseEntity.status(errorCode.getHttpStatus()).body(response);
    }

    /** @Valid 검증 실패 - 필드별 i18n 에러 메시지 */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity&amp;lt;ErrorResponse&amp;gt; handleMethodArgumentNotValidException(
            MethodArgumentNotValidException e,
            Locale locale
    ) {
        log.warn(&quot;Validation failed: {}&quot;, e.getMessage());
        ErrorResponse response = ErrorResponse.of(
                ErrorCode.INVALID_INPUT_VALUE, e.getBindingResult(), messageSource, locale
        );
        return ResponseEntity.status(ErrorCode.INVALID_INPUT_VALUE.getHttpStatus()).body(response);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 'Locale locale' 파라미터를 선언하기만 해도 Spring에서 자동으로 Locale을 주입해 줍니다.(main에 AcceptHeaderLocaleResolver에서 진행)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. 각 Controller에 Locale 파리미터 추가&lt;/p&gt;
&lt;pre id=&quot;code_1776180576879&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Slf4j
@RestController
@RequestMapping(&quot;/api/test&quot;)
public class TestController {

    private final TestService testService;
    private final MessageSource messageSource;

    @PostMapping()
    public ResponseEntity&amp;lt;ApiResponse&amp;lt;TestResponseDto&amp;gt;&amp;gt; test(
            @Valid @RequestBody TestRequestDto request,
            Locale locale  // Accept-Language 헤더에서 자동 주입
    ) {
        TestResponseDto response = testService.test(request);

        return ResponseEntity.ok(
                ApiResponse.success(&quot;test.response.success&quot;, response, messageSource, locale)
        );
    }
...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. DTO message를 key로 변경&lt;/p&gt;
&lt;pre id=&quot;code_1776180708095&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TestRequestDto {

    @NotBlank(message = &quot;{test.request.dto.invalid.number}&quot;)
    @Size(max = 100, message = &quot;{test.request.dto.invalid.number.max}&quot;)
    private String number;

    @NotBlank(message = &quot;{test.request.dto.invalid.name}&quot;)
    private String name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- {}를 해주는 이유는 ErrorResponse에서 패턴으로 구분하기 위함입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9. Locale 유틸리티 클래스 생성&lt;/p&gt;
&lt;pre id=&quot;code_1776180797211&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public final class LocaleUtils {

    private LocaleUtils() {
        throw new UnsupportedOperationException(&quot;Utility class&quot;);
    }

    /** Locale -&amp;gt; 언어 코드 변환 (ko, en, jp) */
    public static String getLanguageCode(Locale locale) {
        if (locale == null) return &quot;ko&quot;;
        String language = locale.getLanguage().toLowerCase();
        if (language.equals(&quot;ja&quot;)) return &quot;jp&quot;; // 일본어 코드 통일
        return language;
    }

    /** 한국어 Locale인지 확인 */
    public static boolean isKorean(Locale locale) {
        if (locale == null) return true;
        return locale.getLanguage().toLowerCase().equals(&quot;ko&quot;);
    }

    /** 지원 언어 확인 */
    public static boolean isSupportedLanguage(String languageCode) {
        if (languageCode == null) return false;
        return languageCode.equals(&quot;ko&quot;) || languageCode.equals(&quot;en&quot;) || languageCode.equals(&quot;jp&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서비스 레이어에서 Locale 기반으로 분기 처리가 필요한 경우를 위한 유틸리티입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10. DB 칼럼 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 제일 고민이었습니다. 칼럼을 생성할지 테이블을 분리해서 추가, 수정 등을 진행할지 하지만 현재 서비스에서 테이블을 증가 시켜서 관리포인트를 늘리는것보다 필요한 데이터를 jsonb 타입으로 컬럼을 만들어서 관리하는 것이 맞다고 생각하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 방식이 꼭 정답이다고 말하기는 어렵지만 결국에는 고민하고 실행을 한 결과물이 최고의 선택이라고 믿어야 하며, 혹시라도 이후에 수정을 하거나 문제가 발생한다면, 후회보다는 한번 더 배움이 있었다고 생각하면 좋을 거 같습니다. 물론 저도 항상 후회하지만 계속 배우고 성장하려고 노력하고 있기 때문에 모두 같이 힘냈으면 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sundaland.tistory.com/693&quot;&gt;https://sundaland.tistory.com/693&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769437330270&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Adding internationalization into the licensing service&quot; data-og-description=&quot;  Spring Boot 마이크로서비스 국제화(i18n) 적용하기  1. 국제화의 필요성국제화(i18n, internationalization)는 다양한 언어와 지역 환경을 지원하는 애플리케이션을 개발하는 과정입니다.  사용자가&quot; data-og-host=&quot;sundaland.tistory.com&quot; data-og-source-url=&quot;https://sundaland.tistory.com/693&quot; data-og-url=&quot;https://sundaland.tistory.com/693&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bl6wC1/dJMb9fZpMVT/q5kk4jkC3ny7mQS3OUxka1/img.png?width=800&amp;amp;height=335&amp;amp;face=0_0_800_335,https://scrap.kakaocdn.net/dn/dNLgl2/dJMb9lL54Nf/e37hMTcRGkM5UBrdXgOrx1/img.png?width=800&amp;amp;height=335&amp;amp;face=0_0_800_335,https://scrap.kakaocdn.net/dn/mTGAy/dJMb9iaLCLy/Z9QgFT75pwEhsHy5fQnAUK/img.png?width=1395&amp;amp;height=593&amp;amp;face=0_0_1395_593&quot;&gt;&lt;a href=&quot;https://sundaland.tistory.com/693&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sundaland.tistory.com/693&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bl6wC1/dJMb9fZpMVT/q5kk4jkC3ny7mQS3OUxka1/img.png?width=800&amp;amp;height=335&amp;amp;face=0_0_800_335,https://scrap.kakaocdn.net/dn/dNLgl2/dJMb9lL54Nf/e37hMTcRGkM5UBrdXgOrx1/img.png?width=800&amp;amp;height=335&amp;amp;face=0_0_800_335,https://scrap.kakaocdn.net/dn/mTGAy/dJMb9iaLCLy/Z9QgFT75pwEhsHy5fQnAUK/img.png?width=1395&amp;amp;height=593&amp;amp;face=0_0_1395_593');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Adding internationalization into the licensing service&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Spring Boot 마이크로서비스 국제화(i18n) 적용하기  1. 국제화의 필요성국제화(i18n, internationalization)는 다양한 언어와 지역 환경을 지원하는 애플리케이션을 개발하는 과정입니다.  사용자가&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sundaland.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@minseojo/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EA%B5%AD%EC%A0%9C%ED%99%94&quot;&gt;https://velog.io/@minseojo/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EA%B5%AD%EC%A0%9C%ED%99%94&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769437342711&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring Boot로 글로벌 서비스를 위한 다국어(국제화) 설정 1 - LocaleResolver&quot; data-og-description=&quot;LocaleResovler 인터페이스, AcceptHeaderLocaleResolver 클래스, SessionLocaleResolver 클래스, CookieLocaleResolver 클래스, FixedLocaleResolver 클래스&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@minseojo/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EA%B5%AD%EC%A0%9C%ED%99%94&quot; data-og-url=&quot;https://velog.io/@minseojo/스프링부트-국제화&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bzHqIN/dJMb86nR0Av/kPxTMvnkKq4f1T57mCHK40/img.png?width=904&amp;amp;height=420&amp;amp;face=0_0_904_420,https://scrap.kakaocdn.net/dn/bLvX1W/dJMb82eHu7A/YKb9Kmiib6C9zkdvT0bdok/img.png?width=904&amp;amp;height=420&amp;amp;face=0_0_904_420,https://scrap.kakaocdn.net/dn/T6529/dJMb87f0PMc/g0b8QgZHSlyzrX0FEBBpb1/img.png?width=904&amp;amp;height=420&amp;amp;face=0_0_904_420&quot;&gt;&lt;a href=&quot;https://velog.io/@minseojo/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EA%B5%AD%EC%A0%9C%ED%99%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@minseojo/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EA%B5%AD%EC%A0%9C%ED%99%94&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bzHqIN/dJMb86nR0Av/kPxTMvnkKq4f1T57mCHK40/img.png?width=904&amp;amp;height=420&amp;amp;face=0_0_904_420,https://scrap.kakaocdn.net/dn/bLvX1W/dJMb82eHu7A/YKb9Kmiib6C9zkdvT0bdok/img.png?width=904&amp;amp;height=420&amp;amp;face=0_0_904_420,https://scrap.kakaocdn.net/dn/T6529/dJMb87f0PMc/g0b8QgZHSlyzrX0FEBBpb1/img.png?width=904&amp;amp;height=420&amp;amp;face=0_0_904_420');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot로 글로벌 서비스를 위한 다국어(국제화) 설정 1 - LocaleResolver&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;LocaleResovler 인터페이스, AcceptHeaderLocaleResolver 클래스, SessionLocaleResolver 클래스, CookieLocaleResolver 클래스, FixedLocaleResolver 클래스&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/spring-boot</category>
      <category>i18n</category>
      <category>Spring-boot</category>
      <category>다국어</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/441</guid>
      <comments>https://baam.tistory.com/441#entry441comment</comments>
      <pubDate>Wed, 15 Apr 2026 00:46:04 +0900</pubDate>
    </item>
    <item>
      <title>2~3주 스프린트로 개발 프로세스를 운영해 본 후기</title>
      <link>https://baam.tistory.com/440</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프린트 기반으로 업무를 운영하면서 가장 크게 느낀 점은 팀의 업무 흐름이 훨씬 명확해졌다는 것이었습니다.&lt;/p&gt;
&lt;p data-end=&quot;456&quot; data-start=&quot;293&quot; data-ke-size=&quot;size16&quot;&gt;Agile 방식을 적용하기 전에는 기능 단위 혹은 요구사항이 생겼을 때 그때 그때 처리하는 방식이였습니다. 기획이 나오면 개발을 시작하고, 개발을 진행하다 보면 요구사항이 조금씩 바뀌거나 추가되는 일이 반복되습니다. 이 과정에서 작업의 우선순위가 애매해지거나 이미 진행하던 개발 방향을 수정해야 하는 상황도 종종 발생했습니다.&lt;/p&gt;
&lt;p data-end=&quot;575&quot; data-start=&quot;458&quot; data-ke-size=&quot;size16&quot;&gt;그래서 팀에서는 2~3주 단위 스프린트 방식으로 업무를 진행하기 시작했습니다. 처음에는 단순히 업무를 기간 단위로 나누는 것 정도로 생각했지만, 실제로 운영해보니 협업 방식 자체가 조금씩 달라지기 시작했습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-end=&quot;603&quot; data-start=&quot;582&quot; data-section-id=&quot;qoe2xo&quot; data-ke-size=&quot;size23&quot;&gt;1. 요구사항이 훨씬 명확해졌다&lt;/h3&gt;
&lt;p data-end=&quot;672&quot; data-start=&quot;605&quot; data-ke-size=&quot;size16&quot;&gt;스프린트를 시작하기 전에 진행하는 스프린트 플래닝 과정에서 이번 스프린트에서 진행할 작업들을 함께 정리했습니다.&lt;/p&gt;
&lt;p data-end=&quot;750&quot; data-start=&quot;674&quot; data-ke-size=&quot;size16&quot;&gt;이 과정에서 기획자와 개발자가 요구사항을 다시 확인하고 구현 범위를 구체적으로 정의하게 되는데, 이 단계가 생각보다 큰 효과를 만들었습니다.&lt;/p&gt;
&lt;p data-end=&quot;821&quot; data-start=&quot;752&quot; data-ke-size=&quot;size16&quot;&gt;이전에는 개발을 진행하다가&lt;br /&gt;&amp;ldquo;이 기능이 정확히 어떤 동작을 해야 하지?&amp;rdquo;&lt;br /&gt;라는 질문이 중간에 발생하는 경우가 있었습니다.&lt;/p&gt;
&lt;p data-end=&quot;925&quot; data-start=&quot;823&quot; data-ke-size=&quot;size16&quot;&gt;하지만 스프린트 단위로 작업을 정의하면서 개발 전에 요구사항을 충분히 정리하는 과정이 자연스럽게 만들어졌고, 그 결과 개발 도중에 발생하는 커뮤니케이션 비용이 크게 줄어들었습니다.&lt;/p&gt;
&lt;p data-end=&quot;925&quot; data-start=&quot;823&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;961&quot; data-start=&quot;932&quot; data-section-id=&quot;1hk6s2e&quot; data-ke-size=&quot;size23&quot;&gt;2. 우선순위 기반으로 일이 진행되기 시작했다&lt;/h3&gt;
&lt;p data-end=&quot;1014&quot; data-start=&quot;963&quot; data-ke-size=&quot;size16&quot;&gt;스프린트를 운영하면서 흥미로웠던 점은 기획자의 업무 방식도 함께 변화했다는 점이었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1061&quot; data-start=&quot;1016&quot; data-ke-size=&quot;size16&quot;&gt;스프린트라는 기준이 생기면서 기획자는 자연스럽게 다음과 같은 판단을 하게 되었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1131&quot; data-start=&quot;1063&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1088&quot; data-start=&quot;1063&quot; data-section-id=&quot;c3ty48&quot;&gt;이 기능이 이번 스프린트에 꼭 필요한가&lt;/li&gt;
&lt;li data-end=&quot;1109&quot; data-start=&quot;1089&quot; data-section-id=&quot;bl27xo&quot;&gt;다음 스프린트로 넘겨도 되는가&lt;/li&gt;
&lt;li data-end=&quot;1131&quot; data-start=&quot;1110&quot; data-section-id=&quot;1oi4pfc&quot;&gt;우선순위가 더 높은 기능은 무엇인가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1220&quot; data-start=&quot;1133&quot; data-ke-size=&quot;size16&quot;&gt;이전에는 새로운 요구사항이 생기면 바로 개발 논의가 시작되는 경우도 있었지만, 이제는 스프린트 단위 안에서 기능의 중요도를 기준으로 판단하게 되었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1281&quot; data-start=&quot;1222&quot; data-ke-size=&quot;size16&quot;&gt;덕분에 개발팀도 갑작스러운 작업 변경 없이 예측 가능한 일정 안에서 개발을 진행할 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1281&quot; data-start=&quot;1222&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1321&quot; data-start=&quot;1288&quot; data-section-id=&quot;19tu8uh&quot; data-ke-size=&quot;size23&quot;&gt;3. 2~3주 스프린트는 생각보다 적절한 사이클이었다&lt;/h3&gt;
&lt;p data-end=&quot;1346&quot; data-start=&quot;1323&quot; data-ke-size=&quot;size16&quot;&gt;스프린트 기간을 정할 때도 고민이 있었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1393&quot; data-start=&quot;1348&quot; data-ke-size=&quot;size16&quot;&gt;너무 짧으면 개발 흐름이 끊기고&lt;br /&gt;너무 길면 피드백이 늦어질 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-end=&quot;1427&quot; data-start=&quot;1395&quot; data-ke-size=&quot;size16&quot;&gt;결과적으로 2~3주 스프린트는 다음과 같은 장점이 있었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1515&quot; data-start=&quot;1429&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1458&quot; data-start=&quot;1429&quot; data-section-id=&quot;1qbcztd&quot;&gt;기능 단위 결과물을 비교적 빠르게 확인할 수 있다&lt;/li&gt;
&lt;li data-end=&quot;1490&quot; data-start=&quot;1459&quot; data-section-id=&quot;1d1bx2g&quot;&gt;스프린트 회고를 통해 프로세스를 계속 개선할 수 있다&lt;/li&gt;
&lt;li data-end=&quot;1515&quot; data-start=&quot;1491&quot; data-section-id=&quot;odwfqx&quot;&gt;팀이 일정한 개발 리듬을 유지할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1562&quot; data-start=&quot;1517&quot; data-ke-size=&quot;size16&quot;&gt;특히 스프린트가 반복되면서 팀 전체의 업무 리듬이 맞춰지는 느낌이 생겼습니다.&lt;/p&gt;
&lt;p data-end=&quot;1562&quot; data-start=&quot;1517&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1605&quot; data-start=&quot;1569&quot; data-section-id=&quot;6lycbx&quot; data-ke-size=&quot;size23&quot;&gt;4. 결국 Agile의 핵심은 &amp;ldquo;같은 리듬으로 일하는 것&amp;rdquo;&lt;/h3&gt;
&lt;p data-end=&quot;1662&quot; data-start=&quot;1607&quot; data-ke-size=&quot;size16&quot;&gt;이번 경험을 통해 느낀 점은 Agile이 단순히 프로세스를 도입하는 것이 아니라는 것이었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1697&quot; data-start=&quot;1664&quot; data-ke-size=&quot;size16&quot;&gt;스프린트를 기준으로 업무를 운영하다 보니 팀원들이 자연스럽게&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1751&quot; data-start=&quot;1699&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1714&quot; data-start=&quot;1699&quot; data-section-id=&quot;n6cjxg&quot;&gt;같은 목표를 바라보고&lt;/li&gt;
&lt;li data-end=&quot;1732&quot; data-start=&quot;1715&quot; data-section-id=&quot;fmuqm0&quot;&gt;같은 일정 안에서 일하고&lt;/li&gt;
&lt;li data-end=&quot;1751&quot; data-start=&quot;1733&quot; data-section-id=&quot;vo6s48&quot;&gt;같은 시점에 결과물을 확인하는&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1763&quot; data-start=&quot;1753&quot; data-ke-size=&quot;size16&quot;&gt;구조가 만들어졌습니다.&lt;/p&gt;
&lt;p data-end=&quot;1825&quot; data-start=&quot;1765&quot; data-ke-size=&quot;size16&quot;&gt;그 결과 업무 속도도 빨라졌고, 무엇보다 서로의 요구사항과 작업 범위를 더 명확하게 이해하게 되었습니다.&lt;/p&gt;
&lt;p data-end=&quot;1825&quot; data-start=&quot;1765&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1825&quot; data-start=&quot;1765&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1825&quot; data-start=&quot;1765&quot; data-ke-size=&quot;size16&quot;&gt;완벽한 방식이라고 말할 수는 없지만, 스프린트를 통해 팀이 같은 리듬으로 협업할 수 있는 환경이 만들어졌다는 점만으로도 충분히 의미 있는 변화였습니다.&lt;/p&gt;</description>
      <category>공부/agile</category>
      <category>Agile</category>
      <category>스프린트</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/440</guid>
      <comments>https://baam.tistory.com/440#entry440comment</comments>
      <pubDate>Sat, 7 Mar 2026 15:58:36 +0900</pubDate>
    </item>
    <item>
      <title>[Grafana] Slack Alert</title>
      <link>https://baam.tistory.com/439</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Grafana는 메트릭, 로그 등 다양한 데이터를 시각화할 수 있는 도구입니다. 시각화뿐만 아니라 알림을 통해서 현재 서버의 상황을 파악할 수 있습니다. 저는 이번에 알림 기능을 활용하여&amp;nbsp;에러 발생을 슬랙으로 확인할 수 있도록 설정하였습니다. 물론 시장에서는 DataDog, Sentry 같은 좋은 서비스들이 있지만, 스타트업이나 비용을 고려해야 한다면 Grafana로도 충분히 가능하다고 볼 수 있을 거 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;혹시라도 부정확한&lt;span&gt;&amp;nbsp;&lt;/span&gt;정보를 전달드릴 수 있습니다. 다만 틀린 거나 부정확한 정보가 있다면 댓글을 남겨주세요.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Slack 채널 생성 및 웹훅 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 슬랙 API(&lt;a href=&quot;https://api.slack.com/apps&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;)에서 앱이 있으시면 생성하시고 없으시다면 만드시면 됩니다.(저는 있는 앱을 사용했습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 앱을 선택하면 앱관련 설정들이 나옵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pXyqd/dJMcab3U80d/Esh6K9AzZ34YoLcizloPNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pXyqd/dJMcab3U80d/Esh6K9AzZ34YoLcizloPNK/img.png&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;766&quot; data-is-animation=&quot;false&quot; width=&quot;394&quot; height=&quot;212&quot; style=&quot;width: 75.0236%; margin-right: 10px;&quot; data-widthpercent=&quot;75.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pXyqd/dJMcab3U80d/Esh6K9AzZ34YoLcizloPNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpXyqd%2FdJMcab3U80d%2FEsh6K9AzZ34YoLcizloPNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1422&quot; height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVfqkd/dJMcaajD6pj/qNTp6FdJ6dcRNMwJADcAAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVfqkd/dJMcaajD6pj/qNTp6FdJ6dcRNMwJADcAAk/img.png&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;930&quot; data-is-animation=&quot;false&quot; style=&quot;width: 23.8136%;&quot; data-widthpercent=&quot;24.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVfqkd/dJMcaajD6pj/qNTp6FdJ6dcRNMwJADcAAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVfqkd%2FdJMcaajD6pj%2FqNTp6FdJ6dcRNMwJADcAAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;930&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Incoming Webhooks를 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wZPK5/dJMcacodUoH/CCkZau9cRLNCP5KHQM0L7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wZPK5/dJMcacodUoH/CCkZau9cRLNCP5KHQM0L7k/img.png&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;740&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.1533%; margin-right: 10px;&quot; data-widthpercent=&quot;44.67&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wZPK5/dJMcacodUoH/CCkZau9cRLNCP5KHQM0L7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwZPK5%2FdJMcacodUoH%2FCCkZau9cRLNCP5KHQM0L7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1432&quot; height=&quot;740&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZT030/dJMcagqCqIA/bJrGVdkXAPw9oerxlduCkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZT030/dJMcagqCqIA/bJrGVdkXAPw9oerxlduCkk/img.png&quot; data-origin-width=&quot;1438&quot; data-origin-height=&quot;600&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.6839%;&quot; data-widthpercent=&quot;55.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZT030/dJMcagqCqIA/bJrGVdkXAPw9oerxlduCkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZT030%2FdJMcagqCqIA%2FbJrGVdkXAPw9oerxlduCkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1438&quot; height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 초록색 버튼인 reinstall to app을 선택하고 전달받을 채널을 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 채널은 슬랙에서 직접 생성하시면 웹후크를 위한 채널에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qMUfd/dJMcacPigZL/X97kIGvSTkNmDK8yq76KV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qMUfd/dJMcacPigZL/X97kIGvSTkNmDK8yq76KV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qMUfd/dJMcacPigZL/X97kIGvSTkNmDK8yq76KV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqMUfd%2FdJMcacPigZL%2FX97kIGvSTkNmDK8yq76KV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;378&quot; height=&quot;53&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 웹훅 설정을 하면 해당 채널에서 메시지가 나타납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 웹훅 URL을 복사합니다.(이후에 grafana에 등록이 필요합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Grafana 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zDkxX/dJMcacPihc4/NzAIPCDb3JWQDf1uZOLnAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zDkxX/dJMcacPihc4/NzAIPCDb3JWQDf1uZOLnAK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;794&quot; data-filename=&quot;스크린샷 2026-01-01 오후 3.53.27.png&quot; style=&quot;width: 14.9726%; margin-right: 10px;&quot; data-widthpercent=&quot;15.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zDkxX/dJMcacPihc4/NzAIPCDb3JWQDf1uZOLnAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzDkxX%2FdJMcacPihc4%2FNzAIPCDb3JWQDf1uZOLnAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ldujl/dJMcagqCqYc/KsKPWNHQ04tKeK3ezgVD5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ldujl/dJMcagqCqYc/KsKPWNHQ04tKeK3ezgVD5K/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;696&quot; data-filename=&quot;스크린샷 2026-01-01 오후 3.54.21.png&quot; style=&quot;width: 52.6175%; margin-right: 10px;&quot; data-widthpercent=&quot;53.87&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ldujl/dJMcagqCqYc/KsKPWNHQ04tKeK3ezgVD5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fldujl%2FdJMcagqCqYc%2FKsKPWNHQ04tKeK3ezgVD5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1454&quot; height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD5SaY/dJMcafFf2Px/S6KLzk2lfO6B1G9trabupK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD5SaY/dJMcafFf2Px/S6KLzk2lfO6B1G9trabupK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;1224&quot; data-filename=&quot;스크린샷 2026-01-01 오후 3.55.04.png&quot; style=&quot;width: 30.0844%;&quot; data-widthpercent=&quot;30.8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD5SaY/dJMcafFf2Px/S6KLzk2lfO6B1G9trabupK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD5SaY%2FdJMcafFf2Px%2FS6KLzk2lfO6B1G9trabupK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1462&quot; height=&quot;1224&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Alerting 선택 후 Add contact point를 선택하여 integration을 Slack으로 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Webhook URL에 Slack에서 생성한 웹훅 URL을 입력합니다.(입력 후 우측 상단에 테스트 버튼으로 메시지가 전달이 되는지 확인해 보세요.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 테스트를 확인하셨다면, 알림 준비가 어느 정도 됐다고 생각하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Grafana 알림 정책 수정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-01 오후 4.00.21.png&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B8uQl/dJMcacV3Crz/VbxkslEuIla2IYAhqqNRNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B8uQl/dJMcacV3Crz/VbxkslEuIla2IYAhqqNRNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B8uQl/dJMcacV3Crz/VbxkslEuIla2IYAhqqNRNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB8uQl%2FdJMcacV3Crz%2FVbxkslEuIla2IYAhqqNRNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;277&quot; data-filename=&quot;스크린샷 2026-01-01 오후 4.00.21.png&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- slack 설정으로 default policy를 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Grafana Alert rules 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/90Fom/dJMcai9LtLB/XlLEP8RveO9WAT3JYeMGok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/90Fom/dJMcai9LtLB/XlLEP8RveO9WAT3JYeMGok/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;1040&quot; data-filename=&quot;스크린샷 2026-01-01 오후 4.14.38.png&quot; style=&quot;width: 52.102%; margin-right: 10px;&quot; data-widthpercent=&quot;52.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/90Fom/dJMcai9LtLB/XlLEP8RveO9WAT3JYeMGok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F90Fom%2FdJMcai9LtLB%2FXlLEP8RveO9WAT3JYeMGok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;1040&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zB4TQ/dJMcagEblCX/njPNHZ7CwgifcQvwA8ReO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zB4TQ/dJMcagEblCX/njPNHZ7CwgifcQvwA8ReO0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1396&quot; data-origin-height=&quot;1124&quot; data-filename=&quot;스크린샷 2026-01-01 오후 4.16.17.png&quot; style=&quot;width: 46.7352%;&quot; data-widthpercent=&quot;47.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zB4TQ/dJMcagEblCX/njPNHZ7CwgifcQvwA8ReO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzB4TQ%2FdJMcagEblCX%2FnjPNHZ7CwgifcQvwA8ReO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1396&quot; height=&quot;1124&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 왼쪽 이미지는 Loki에서 수집된 데이터를 기반으로 쿼리를 설정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 오른쪽 이미지는 쿼리를 기반으로 설정해 주는 부분들입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- B는 마지막 값을 선택하며, 여러 데이터 포인트 중 가장 최근 값을 가져옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- C는 B의 결괏값이 0보다 크면 알림을 발생시킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 설정은 에러가 1건이라도 발생하면 slack 알림으로 보낸다고 생각해 주시면 될 거 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLQboa/dJMcafL1hKA/w9jlyIu8KKpb63BS9FY7G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLQboa/dJMcafL1hKA/w9jlyIu8KKpb63BS9FY7G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLQboa/dJMcafL1hKA/w9jlyIu8KKpb63BS9FY7G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLQboa%2FdJMcafL1hKA%2Fw9jlyIu8KKpb63BS9FY7G0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;488&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Folder와 Groupt은 Alert rule을 관리하기 위한 설정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Group은 현재 5분마다 쿼리를 실행해서 조건을 체크합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Pending period는 조건 위반하면 즉시 알림을 발송할지 지속된 상태를 보고 발송할지 설정하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Configure no data and error handling은 데이터가 없으면 No Data 상태로 되는데 이때 Error와 OK로 지정할 수 있습니다.(저의 경우 데이터가 없다면 OK로 지정해서 No data일 때 오류 알림이 오지 않도록 설정했습니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caFdjp/dJMcabJDopZ/aA2hg1FlIOm3d56HhUkWr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caFdjp/dJMcabJDopZ/aA2hg1FlIOm3d56HhUkWr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caFdjp/dJMcabJDopZ/aA2hg1FlIOm3d56HhUkWr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaFdjp%2FdJMcabJDopZ%2FaA2hg1FlIOm3d56HhUkWr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;399&quot; height=&quot;162&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 일부러 오류를 발생하여 테스트를 해본 결과입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-01 오후 5.21.27.png&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SwYew/dJMcabJDovm/iLihJjOdmY4f41RmRYkhy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SwYew/dJMcabJDovm/iLihJjOdmY4f41RmRYkhy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SwYew/dJMcabJDovm/iLihJjOdmY4f41RmRYkhy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSwYew%2FdJMcabJDovm%2FiLihJjOdmY4f41RmRYkhy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;164&quot; data-filename=&quot;스크린샷 2026-01-01 오후 5.21.27.png&quot; data-origin-width=&quot;1294&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Summary와 Description을 수정하면 전달하는 텍스트도 변경할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Summary와 Description으로 슬랙에 알림을 전달하려면 Contact Points에 Title과 Body에 값을 넣어주면 직접 작성한 텍스트로 전달할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리를 추가해서 알림을 더 세분화할 수 있다고 생각합니다. 초기 설정과 테스트까지의 기록입니다. 각자의 서비스에 맞는 알림을 구현하는 것이 좋을 것이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pixx.tistory.com/339&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pixx.tistory.com/339&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1767255834936&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Grafana] Grafana와 Slack 연동하여 Alert 설정하기&quot; data-og-description=&quot;현대 IT 환경에서 시스템의 성능과 상태를 실시간으로 모니터링하는 것은 매우 중요합니다. 그러나 문제 발생 시, 그 상황을 빠르게 파악하고 대응하는 것이 더욱 중요합니다.&amp;nbsp;Grafana는 이러한 &quot; data-og-host=&quot;pixx.tistory.com&quot; data-og-source-url=&quot;https://pixx.tistory.com/339&quot; data-og-url=&quot;https://pixx.tistory.com/339&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/09E14/hyZPJR7tfp/EmHuV6K9tfD6wSbotz1ZoK/img.jpg?width=222&amp;amp;height=227&amp;amp;face=0_0_222_227,https://scrap.kakaocdn.net/dn/49ocI/hyZPRiksyI/ukKrbLdkbxdzeZVqnKB1w1/img.jpg?width=222&amp;amp;height=227&amp;amp;face=0_0_222_227,https://scrap.kakaocdn.net/dn/vBsK1/hyZQxj2Axx/0Qc71Gci6ty5YkDek1PeyK/img.png?width=1050&amp;amp;height=599&amp;amp;face=0_0_1050_599&quot;&gt;&lt;a href=&quot;https://pixx.tistory.com/339&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pixx.tistory.com/339&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/09E14/hyZPJR7tfp/EmHuV6K9tfD6wSbotz1ZoK/img.jpg?width=222&amp;amp;height=227&amp;amp;face=0_0_222_227,https://scrap.kakaocdn.net/dn/49ocI/hyZPRiksyI/ukKrbLdkbxdzeZVqnKB1w1/img.jpg?width=222&amp;amp;height=227&amp;amp;face=0_0_222_227,https://scrap.kakaocdn.net/dn/vBsK1/hyZQxj2Axx/0Qc71Gci6ty5YkDek1PeyK/img.png?width=1050&amp;amp;height=599&amp;amp;face=0_0_1050_599');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Grafana] Grafana와 Slack 연동하여 Alert 설정하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현대 IT 환경에서 시스템의 성능과 상태를 실시간으로 모니터링하는 것은 매우 중요합니다. 그러나 문제 발생 시, 그 상황을 빠르게 파악하고 대응하는 것이 더욱 중요합니다.&amp;nbsp;Grafana는 이러한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pixx.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/etc</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/439</guid>
      <comments>https://baam.tistory.com/439#entry439comment</comments>
      <pubDate>Thu, 1 Jan 2026 17:25:31 +0900</pubDate>
    </item>
    <item>
      <title>[EC2] Github-Action으로 EC2에 Spring-Boot 배포(Blue-Green) - 2</title>
      <link>https://baam.tistory.com/438</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;지난번에는 AWS 설정하는 부분을 작성하였습니다.(&lt;a href=&quot;https://baam.tistory.com/437&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;)&lt;br /&gt;이번에는 Github-Action과 CodeDeploy Script 관련해서 작성하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;혹시라도 부정확한 정보를 전달드릴 수 있습니다. 다만 틀린 거나 부정확한 정보가 있다면 댓글을 남겨주세요&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Github-Action Work-flow 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- .github 폴더에 workflows 폴더 생성 및 deploy.yml 파일 생성&lt;/p&gt;
&lt;pre id=&quot;code_1766923403751&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 원한시는 Deploy 이름을 작성하시면 됩니다.
name: Blue-Green Deployment with CodeDeploy

on:
  push:
  # 어떤 브랜치에서 진행이 될지 입력합니다.
  # push만 있는게 아닌 PR도 있습니다.
    branches:
      - main
      - develop

env:
  AWS_REGION: ap-northeast-2
  # secrets 설정된 부분은 Github Repo 설정에서 Secrets and variables에 Repository secrets에 작성하시면 됩니다.
  S3_BUCKET: ${{ secrets.S3_DEPLOYMENT_BUCKET }}
  CODEDEPLOY_APP_NAME: ${{secrets.CODEDEPLOY_APP_NAME}}

jobs:
  build-and-deploy:
 	# Github actions의 실행 환경을 지정합니다.
    name: Build and Deploy
    runs-on: ubuntu-latest
    environment:
    # 브랜치에 따라 배포 환경을 설정합니다.
      name: ${{ github.ref == 'refs/heads/main' &amp;amp;&amp;amp; 'production' || 'development' }}
    # 저장소의 코드를 확인합니다.
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

	# Spring-Boot 서버의 java version과 일치하게 설정합니다.
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: 'gradle'
	# gradlew에 실행권한을 부여합니다.
      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
	# 테스트 코드가 있다면 진행하는 부분입니다.
      - name: Build with Gradle
        run: ./gradlew clean build -x test
      
      # 브랜치에 따라 CodeDeploy 배포 그룹 설정합니다.
      - name: Set deployment group
        id: set-deployment-group
        run: |
          if [ &quot;${{ github.ref }}&quot; == &quot;refs/heads/main&quot; ]; then
            echo &quot;CODEDEPLOY_DEPLOYMENT_GROUP=${{ secrets.DEPLOYMENT_GROUP_PROD }}&quot; &amp;gt;&amp;gt; $GITHUB_ENV
          else
            echo &quot;CODEDEPLOY_DEPLOYMENT_GROUP=${{ secrets.DEPLOYMENT_GROUP_DEV }}&quot; &amp;gt;&amp;gt; $GITHUB_ENV
          fi

      # - name: Run tests
      #   run: ./gradlew test

	# AWS 발급받은 키로 로그인 인증하는 부분입니다.(키 발급은 보안 자격 증명에서 발급받아서 사용할 수 있습니다.)
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR (Optional)
        if: false  # ECR 사용시 true로 변경
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

	# 배포 버전 태그와 환경 변수를 생성합니다.
      - name: Generate deployment tag
        run: |
          IMAGE_TAG=$(date +%Y%m%d-%H%M%S)-${GITHUB_SHA::7}
          echo &quot;IMAGE_TAG=$IMAGE_TAG&quot; &amp;gt;&amp;gt; $GITHUB_ENV
          echo &quot;DEPLOYMENT_ID=$(date +%Y%m%d-%H%M%S)&quot; &amp;gt;&amp;gt; $GITHUB_ENV

          # 환경 변수 결정 (GitHub Actions environment에 맞춤)
          if [ &quot;${{ github.ref }}&quot; == &quot;refs/heads/main&quot; ]; then
            DEPLOY_ENV=&quot;prod&quot;
          elif [ &quot;${{ github.ref }}&quot; == &quot;refs/heads/develop&quot; ]; then
            DEPLOY_ENV=&quot;dev&quot;
          else
            DEPLOY_ENV=&quot;dev&quot;
          fi
          echo &quot;DEPLOY_ENV=$DEPLOY_ENV&quot; &amp;gt;&amp;gt; $GITHUB_ENV

	# 배포 패키지를 생성하고 압축합니다.
      - name: Create deployment package
        run: |
          # 배포에 필요한 파일들 복사
          mkdir -p deployment
          # build/libs 디렉토리 구조를 유지하여 복사
          mkdir -p deployment/build/libs
          cp build/libs/*.jar deployment/build/libs/
          cp Dockerfile.deploy deployment/Dockerfile.deploy
          cp docker-compose.blue-green.${{ env.DEPLOY_ENV }}.yml deployment/
          cp appspec.yml deployment/
          cp -r scripts deployment/
          cp -r conf deployment/

          # 스크립트 파일에 실행 권한 부여
          find deployment/scripts -name &quot;*.sh&quot; -type f -exec chmod +x {} \;

          # 환경변수를 .env 파일로 저장
          echo &quot;IMAGE_TAG=${{ env.IMAGE_TAG }}&quot; &amp;gt; deployment/.env.deploy
          echo &quot;DEPLOY_ENV=${{ env.DEPLOY_ENV }}&quot; &amp;gt;&amp;gt; deployment/.env.deploy

          # zip으로 압축
          cd deployment
          zip -r ../deployment-${{ env.DEPLOYMENT_ID }}.zip .
          cd ..

	# 배포 패키지를 S3에 업로드 합니다.
      - name: Upload to S3
        run: |
          aws s3 cp deployment-${{ env.DEPLOYMENT_ID }}.zip \
            s3://${{ env.S3_BUCKET }}/deployment-${{ env.DEPLOYMENT_ID }}.zip

	# CodeDeploy 배포를 생성하고 실행합니다.
      - name: Create CodeDeploy deployment
        id: deploy
        run: |
          DEPLOYMENT_ID=$(aws deploy create-deployment \
            --application-name ${{ env.CODEDEPLOY_APP_NAME }} \
            --deployment-group-name ${{ env.CODEDEPLOY_DEPLOYMENT_GROUP }} \
            --s3-location bucket=${{ env.S3_BUCKET }},key=deployment-${{ env.DEPLOYMENT_ID }}.zip,bundleType=zip \
            --description &quot;Deployment from GitHub Actions - ${{ github.sha }}&quot; \
            --query 'deploymentId' \
            --output text)

          echo &quot;DEPLOYMENT_ID=$DEPLOYMENT_ID&quot; &amp;gt;&amp;gt; $GITHUB_ENV
          echo &quot;deployment_id=$DEPLOYMENT_ID&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT

	# 배포가 완료될 때까지 대기합니다.
      - name: Wait for deployment to complete
        run: |
          echo &quot;Waiting for deployment ${{ env.DEPLOYMENT_ID }} to complete...&quot;
          aws deploy wait deployment-successful --deployment-id ${{ env.DEPLOYMENT_ID }}

	# 배포 상태를 조회하여 출력합니다.
      - name: Get deployment status
        if: always()
        run: |
          aws deploy get-deployment --deployment-id ${{ env.DEPLOYMENT_ID }}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/elLGcr/dJMcaaRsBJR/IEDgWiOb3yutjofxdAf2OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/elLGcr/dJMcaaRsBJR/IEDgWiOb3yutjofxdAf2OK/img.png&quot; data-alt=&quot;Repository secrets&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/elLGcr/dJMcaaRsBJR/IEDgWiOb3yutjofxdAf2OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FelLGcr%2FdJMcaaRsBJR%2FIEDgWiOb3yutjofxdAf2OK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;245&quot; data-origin-width=&quot;1478&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Repository secrets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Repository secrets입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- secrets으로 가져오는 값은 Repository secrets에 등록이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. CodeDeploy Script 작성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3orY5/dJMcadgiKLV/aPgKpPIkSXbYKLbR5N3Nf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3orY5/dJMcadgiKLV/aPgKpPIkSXbYKLbR5N3Nf1/img.png&quot; data-alt=&quot;폴더구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3orY5/dJMcadgiKLV/aPgKpPIkSXbYKLbR5N3Nf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3orY5%2FdJMcadgiKLV%2FaPgKpPIkSXbYKLbR5N3Nf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;76&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;폴더구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저는 이미지와 같은 폴더 구조로 구성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- script의 진행 순서는 AWS 공식문서에 잘 작성 돼 있습니다.(&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html#appspec-hooks-server&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-1. before_install.sh 생성 및 코드 입력&lt;/p&gt;
&lt;pre id=&quot;code_1766925970334&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

# ============================================
# CodeDeploy BeforeInstall Hook
# 배포 전 준비 작업
# ============================================

set -e

echo &quot;=========================================&quot;
echo &quot;BeforeInstall: 배포 전 준비 작업 시작&quot;
echo &quot;=========================================&quot;

# 배포 디렉토리 백업 및 정리
if [ -d /home/ubuntu/test ]; then
    echo &quot;기존 배포 디렉토리 백업 중...&quot;
    BACKUP_DIR=&quot;/home/ubuntu/backup/test-backup-$(date +%Y%m%d-%H%M%S)&quot;
    sudo cp -r /home/ubuntu/test &quot;$BACKUP_DIR&quot;
    echo &quot;백업 완료: $BACKUP_DIR&quot;

    # 백업 개수 제한 (최대 5개 유지)
    echo &quot;백업 개수 확인 및 정리 중...&quot;
    BACKUP_COUNT=$(find /home/ubuntu/backup -maxdepth 1 -type d -name &quot;test-backup-*&quot; | wc -l)
    echo &quot;현재 백업 개수: $BACKUP_COUNT&quot;
    
    # 제거하지 않으면 SSD 용량 문제가 발생할 수 있습니다.
    if [ &quot;$BACKUP_COUNT&quot; -gt 5 ]; then
        echo &quot;백업 개수가 5개를 초과합니다. 오래된 백업 삭제 중...&quot;
        # 가장 오래된 백업부터 삭제 (이름의 날짜/시간 순서로 정렬)
        find /home/ubuntu/backup -maxdepth 1 -type d -name &quot;test-backup-*&quot; | sort | head -n -5 | while read -r old_backup; do
            echo &quot;오래된 백업 삭제: $old_backup&quot;
            sudo rm -rf &quot;$old_backup&quot;
        done
        echo &quot;백업 정리 완료&quot;
    fi
    
    # 기존 디렉토리 삭제 (CodeDeploy가 새 파일을 배포할 수 있도록 정리)
    echo &quot;기존 배포 디렉토리 삭제 중...&quot;
    sudo rm -rf /home/ubuntu/test
    echo &quot;기존 디렉토리 삭제 완료&quot;
fi

# 배포 디렉토리 생성
sudo mkdir -p /home/ubuntu/test
# 권한 설정
sudo chown -R ubuntu:ubuntu /home/ubuntu/test

# Docker 이미지 정리 (오래된 이미지 제거)
# 오래된 이미지를 제거하지 않으면 디스크 용량 부족 문제가 발생할 수 있습니다.
echo &quot;오래된 Docker 이미지 정리 중...&quot;
docker image prune -f --filter &quot;until=72h&quot; || true

echo &quot;BeforeInstall 완료&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-2. After_install.sh 생성 및 코드 입력&lt;/p&gt;
&lt;pre id=&quot;code_1766926756016&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

# ============================================
# CodeDeploy AfterInstall Hook
# 파일 설치 후 환경 설정
# ============================================

set -e

echo &quot;=========================================&quot;
echo &quot;AfterInstall: 환경 설정 시작&quot;
echo &quot;=========================================&quot;

cd /home/ubuntu/test

# GitHub Actions에서 전달한 배포 환경 변수 로드 (.env.deploy 파일)
# 이 파일에는 IMAGE_TAG, DEPLOY_ENV 등이 포함되어 있습니다
if [ -f .env.deploy ]; then
    export $(cat .env.deploy | xargs)
    echo &quot;배포 환경 변수 로드 완료&quot;
    echo &quot;  - IMAGE_TAG: ${IMAGE_TAG}&quot;
    echo &quot;  - DEPLOY_ENV: ${DEPLOY_ENV}&quot;
fi

# 환경 결정 (dev 또는 prod)
# DEPLOY_ENV가 없으면 기본값으로 'dev' 사용
DEPLOY_ENV=${DEPLOY_ENV:-dev}
PARAMETER_NAME=&quot;/test/${DEPLOY_ENV}/env&quot;

echo &quot;환경: ${DEPLOY_ENV}&quot;
echo &quot;Parameter Store 경로: ${PARAMETER_NAME}&quot;

# 환경 변수 파일을 저장할 디렉토리 생성
mkdir -p env

# Parameter Store에서 환경 변수 가져와서 .env 파일 생성
echo &quot;Parameter Store에서 환경 변수 가져오는 중...&quot;

# .env 파일 생성(예: env/.env.dev 또는 env/.env.prod)
ENV_FILE=&quot;env/.env.${DEPLOY_ENV}&quot;

# 파일 초기화(기존 내용 삭제)
&amp;gt; &quot;$ENV_FILE&quot;

# AWS Systems Manager Parameter Store에서 환경 변수 전체 가져오기
# --with-decryption: SecureString 타입지정시 파라미터 복호화 필요합니다.
echo &quot;파라미터 가져오는 중: ${PARAMETER_NAME}&quot;
ENV_CONTENT=$(aws ssm get-parameter \
  --name &quot;${PARAMETER_NAME}&quot; \
  --with-decryption \
  --query 'Parameter.Value' \
  --output text \
  --region ap-northeast-2 2&amp;gt;&amp;amp;1)

AWS_EXIT_CODE=$?

# 에러 체크
if [ $AWS_EXIT_CODE -ne 0 ]; then
    echo &quot;ERROR: Parameter Store에서 환경 변수를 가져올 수 없습니다: ${PARAMETER_NAME}&quot;
    echo &quot;Exit code: $AWS_EXIT_CODE&quot;
    echo &quot;에러 메시지: ${ENV_CONTENT}&quot;
    exit 1
fi

# Parameter 값이 비어있는지 확인합니다.
if [ -z &quot;$ENV_CONTENT&quot; ] || [ &quot;$ENV_CONTENT&quot; == &quot;None&quot; ]; then
    echo &quot;ERROR: Parameter Store 값이 비어있습니다: ${PARAMETER_NAME}&quot;
    echo &quot;받은 값: '${ENV_CONTENT}'&quot;
    exit 1
fi

echo &quot;환경 변수 파싱 중...&quot;

# 임시 파일에 저장합니다. (while loop에서 읽기 위함)
TEMP_ENV=$(mktemp)
echo &quot;$ENV_CONTENT&quot; &amp;gt; &quot;$TEMP_ENV&quot;

# 환경 변수 파싱 및 .env 파일에 저장
# 환경 변수를 한 줄씩 읽어서 처리
while IFS= read -r line || [ -n &quot;$line&quot; ]; do
    # 주석 라인 제거 (#으로 시작하는 라인)
    if [[ &quot;$line&quot; =~ ^[[:space:]]*# ]]; then
        continue
    fi
    
    # 빈 라인 제거
    line_trimmed=$(echo &quot;$line&quot; | xargs)
    if [ -z &quot;$line_trimmed&quot; ]; then
        continue
    fi
    
    # KEY=VALUE 형태인지 확인
    if [[ &quot;$line&quot; =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
        KEY=&quot;${BASH_REMATCH[1]}&quot;
        VALUE=&quot;${BASH_REMATCH[2]}&quot;
        
        # 앞뒤 공백 제거
        VALUE=$(echo &quot;$VALUE&quot; | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
        
        # 따옴표 제거 (있는 경우)
        VALUE=$(echo &quot;$VALUE&quot; | sed &quot;s/^['\&quot;]//;s/['\&quot;]$//&quot;)
        
        echo &quot;${KEY}=${VALUE}&quot; &amp;gt;&amp;gt; &quot;$ENV_FILE&quot;
        echo &quot;  ✓ ${KEY} 설정 완료&quot;
    else
	    # KEY=VALUE 형태가 아닌 라인은 무시
        echo &quot;  ⚠ 무시된 라인: $line&quot;
    fi
done &amp;lt; &quot;$TEMP_ENV&quot;

# 임시 파일 삭제
rm -f &quot;$TEMP_ENV&quot;

# GitHub Actions에서 전달받은 IMAGE_TAG를 .env 파일에 추가
# docker-compose.yml에서 이 값을 사용하여 올바른 이미지 버전을 실행
if [ -n &quot;$IMAGE_TAG&quot; ]; then
    echo &quot;IMAGE_TAG=${IMAGE_TAG}&quot; &amp;gt;&amp;gt; &quot;$ENV_FILE&quot;
    echo &quot;  ✓ IMAGE_TAG 설정 완료: ${IMAGE_TAG}&quot;
fi

echo &quot;환경 변수 파일 생성 완료: ${ENV_FILE}&quot;
echo &quot;AfterInstall 완료&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-3. application_start.sh 생성 및 코드 입력&lt;/p&gt;
&lt;pre id=&quot;code_1766927487007&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

# ============================================
# CodeDeploy ApplicationStart Hook
# Blue-Green 배포 실행
# ============================================
# 에러 발생시 즉시 종료
set -e 

echo &quot;=========================================&quot;
echo &quot;ApplicationStart: Blue-Green 배포 시작&quot;
echo &quot;=========================================&quot;

cd /home/ubuntu/test

# 환경 변수 로드
if [ -f .env.deploy ]; then
    export $(cat .env.deploy | xargs)
fi

# DEPLOY_ENV 확인 (기본값: dev)
DEPLOY_ENV=${DEPLOY_ENV:-dev}
echo &quot;배포 환경: ${DEPLOY_ENV}&quot;

# SSM에서 가져온 환경 변수 파일 로드 (ALB 환경 변수 포함)
# ENV_FILE에는 ALB ARN, ALB_TARGET_GROUP_BLUE_ARN, ALB_TARGET_GROUP_GREEN_ARN 등이 작성된 상태여야 합니다.
ENV_FILE=&quot;env/.env.${DEPLOY_ENV}&quot;
if [ -f &quot;$ENV_FILE&quot; ]; then
    echo &quot;환경 변수 파일 로드 중: ${ENV_FILE}&quot;
    # 주석과 빈 라인 제외하고 환경 변수 로드
    export $(cat &quot;$ENV_FILE&quot; | grep -v '^#' | grep -v '^$' | xargs)
    echo &quot;환경 변수 로드 완료&quot;
else
    echo &quot;ERROR: 환경 변수 파일이 없습니다: ${ENV_FILE}&quot;
    echo &quot;after_install.sh가 정상적으로 실행되었는지 확인하세요.&quot;
    exit 1
fi

# 환경 변수 확인
if [ -z &quot;$ALB_TARGET_GROUP_BLUE_ARN&quot; ]; then
    echo &quot;ERROR: ALB_TARGET_GROUP_BLUE_ARN 환경 변수가 설정되지 않았습니다.&quot;
    echo &quot;Parameter Store에 다음 형식으로 추가하세요:&quot;
    echo &quot;  ALB_TARGET_GROUP_BLUE_ARN=arn:aws:elasticloadbalancing:...&quot;
    exit 1
fi

if [ -z &quot;$ALB_TARGET_GROUP_GREEN_ARN&quot; ]; then
    echo &quot;ERROR: ALB_TARGET_GROUP_GREEN_ARN 환경 변수가 설정되지 않았습니다.&quot;
	echo &quot;Parameter Store에 다음 형식으로 추가하세요:&quot;
    echo &quot;  ALB_TARGET_GROUP_GREEN_ARN=arn:aws:elasticloadbalancing:...&quot;
    exit 1
fi

# ALB 리스너 또는 리스너 규칙 ARN 확인(둘 중 하나는 반드시 필요합니다.)
# 저의 경우 하나의 ALB에 LISTENER_RULE을 사용하고 있어서 ALB_LISTENER_RULE_ARN도 parameter에 작성하였습니다.
if [ -z &quot;$ALB_LISTENER_RULE_ARN&quot; ] &amp;amp;&amp;amp; [ -z &quot;$ALB_LISTENER_ARN&quot; ]; then
    echo &quot;설정 방법:&quot;
    echo &quot;  - 리스너 규칙 사용 시: ALB_LISTENER_RULE_ARN=arn:aws:elasticloadbalancing:...&quot;
    echo &quot;  - 리스너 직접 사용 시: ALB_LISTENER_ARN=arn:aws:elasticloadbalancing:...&quot;
    exit 1
fi

# 디버깅: ALB 환경 변수 확인
echo &quot;=========================================&quot;
echo &quot;배포 설정 정보&quot;
echo &quot;=========================================&quot;
echo &quot;배포 환경: ${DEPLOY_ENV}&quot;
echo &quot;이미지 태그: ${IMAGE_TAG}&quot;
echo &quot;&quot;
echo &quot;ALB 설정:&quot;
echo &quot;  Blue Target Group: ${ALB_TARGET_GROUP_BLUE_ARN}&quot;
echo &quot;  Green Target Group: ${ALB_TARGET_GROUP_GREEN_ARN}&quot;

if [ -n &quot;$ALB_LISTENER_RULE_ARN&quot; ]; then
    echo &quot;ALB_LISTENER_RULE_ARN: ${ALB_LISTENER_RULE_ARN}&quot;
else
    echo &quot;ALB_LISTENER_ARN: ${ALB_LISTENER_ARN}&quot;
fi

# Blue-Green 배포 스크립트 실행 및 권한 부여
chmod +x scripts/deploy.sh
./scripts/deploy.sh

echo &quot;ApplicationStart 완료&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- application_start.sh까지가 Deploy를 위한 준비과정이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;- application_start.sh에서 deploy.sh을 실행하여 ec2 인스턴스에서 deploy가 진행되도록 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2-4. deploy.sh&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;생성 및 코드 입력&lt;/p&gt;
&lt;pre id=&quot;code_1766928202403&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#!/bin/bash

# ============================================
# Blue-Green 배포 스크립트
# ALB 타겟 그룹 자동 전환
# ============================================

set -e # 에러 발생시 즉시 종료

# 색상 정의
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 로그 함수
log_info() {
    echo -e &quot;${BLUE}[INFO]${NC} $1&quot;
}

log_success() {
    echo -e &quot;${GREEN}[SUCCESS]${NC} $1&quot;
}

log_error() {
    echo -e &quot;${RED}[ERROR]${NC} $1&quot;
}

log_warning() {
    echo -e &quot;${YELLOW}[WARNING]${NC} $1&quot;
}

# 의존성 확인 함수 추가
check_dependencies() {
    log_info &quot;필수 도구 확인 중...&quot;
    
    # jq 설치 확인 (JSON 파싱에 필요)
    if ! command -v jq &amp;amp;&amp;gt; /dev/null; then
        log_error &quot;jq가 설치되지 않았습니다.&quot;
        log_error &quot;설치 방법: sudo apt-get install -y jq&quot;
        exit 1
    fi
}

# 환경 변수 확인
check_env_vars() {
    log_info &quot;환경 변수 확인 중...&quot;

    if [ -z &quot;$ALB_TARGET_GROUP_BLUE_ARN&quot; ]; then
        log_error &quot;ALB_TARGET_GROUP_BLUE_ARN 환경 변수가 설정되지 않았습니다.&quot;
        exit 1
    fi

    if [ -z &quot;$ALB_TARGET_GROUP_GREEN_ARN&quot; ]; then
        log_error &quot;ALB_TARGET_GROUP_GREEN_ARN 환경 변수가 설정되지 않았습니다.&quot;
        exit 1
    fi

     # 리스너 규칙 ARN 필수
    if [ -z &quot;$ALB_LISTENER_RULE_ARN&quot; ]; then
        log_error &quot;ALB_LISTENER_RULE_ARN 환경 변수가 설정되지 않았습니다.&quot;
        log_error &quot;특정 리스너 규칙에 대해 Blue-Green 배포를 진행합니다.&quot;
        exit 1
    fi

    # ========== 추가: 환경별 Docker Compose 파일 결정 ==========
    # DEPLOY_ENV 확인 (기본값: dev)
    DEPLOY_ENV=${DEPLOY_ENV:-dev}
    log_info &quot;배포 환경: ${DEPLOY_ENV}&quot;

    # 환경에 따른 Docker Compose 파일 설정
    if [ &quot;$DEPLOY_ENV&quot; = &quot;prod&quot; ]; then
        DOCKER_COMPOSE_FILE=&quot;docker-compose.blue-green.prod.yml&quot;
    else
        DOCKER_COMPOSE_FILE=&quot;docker-compose.blue-green.dev.yml&quot;
    fi

    log_info &quot;Docker Compose 파일: ${DOCKER_COMPOSE_FILE}&quot;

    # Docker Compose 파일 존재 확인
    if [ ! -f &quot;$DOCKER_COMPOSE_FILE&quot; ]; then
        log_error &quot;Docker Compose 파일이 존재하지 않습니다: ${DOCKER_COMPOSE_FILE}&quot;
        exit 1
    fi

    log_success &quot;환경 변수 확인 완료&quot;
}

# 현재 활성 환경 감지
get_active_environment() {
    log_info &quot;현재 활성 환경 감지 중...&quot;

    # 전체 규칙 데이터 가져오기
    RULE_JSON=$(aws elbv2 describe-rules \
        --rule-arns &quot;$ALB_LISTENER_RULE_ARN&quot; \
        --output json)
    
    # Weight가 1인 타겟 그룹 찾기
	# Blue-Green 배포에서는 한 쪽이 Weight=1, 다른 쪽이 Weight=0
    CURRENT_TARGET_GROUP=$(echo &quot;$RULE_JSON&quot; | \
        grep -B 1 '&quot;Weight&quot;: 1' | \
        grep '&quot;TargetGroupArn&quot;' | \
        sed 's/.*&quot;TargetGroupArn&quot;: &quot;\([^&quot;]*\)&quot;.*/\1/')
    
    # 값 확인
    log_info &quot;현재 규칙의 타겟 그룹 ARN: '${CURRENT_TARGET_GROUP}'&quot;
    log_info &quot;Blue 타겟 그룹 ARN: '${ALB_TARGET_GROUP_BLUE_ARN}'&quot;
    log_info &quot;Green 타겟 그룹 ARN: '${ALB_TARGET_GROUP_GREEN_ARN}'&quot;

    # ARN 비교 (앞뒤 공백 제거)
    CURRENT_TARGET_GROUP=$(echo &quot;$CURRENT_TARGET_GROUP&quot; | xargs)
    ALB_TARGET_GROUP_BLUE_ARN=$(echo &quot;$ALB_TARGET_GROUP_BLUE_ARN&quot; | xargs)
    ALB_TARGET_GROUP_GREEN_ARN=$(echo &quot;$ALB_TARGET_GROUP_GREEN_ARN&quot; | xargs)

    if [ &quot;$CURRENT_TARGET_GROUP&quot; = &quot;$ALB_TARGET_GROUP_BLUE_ARN&quot; ]; then
        ACTIVE_ENV=&quot;blue&quot;
        INACTIVE_ENV=&quot;green&quot;
        ACTIVE_PORT=8080
        INACTIVE_PORT=8081
    elif [ &quot;$CURRENT_TARGET_GROUP&quot; = &quot;$ALB_TARGET_GROUP_GREEN_ARN&quot; ]; then
        ACTIVE_ENV=&quot;green&quot;
        INACTIVE_ENV=&quot;blue&quot;
        ACTIVE_PORT=8081
        INACTIVE_PORT=8080
    else
        log_error &quot;현재 타겟 그룹이 Blue 또는 Green과 일치하지 않습니다.&quot;
        log_error &quot;현재 타겟 그룹: '${CURRENT_TARGET_GROUP}'&quot;
        log_error &quot;Blue ARN: '${ALB_TARGET_GROUP_BLUE_ARN}'&quot;
        log_error &quot;Green ARN: '${ALB_TARGET_GROUP_GREEN_ARN}'&quot;
        exit 1
    fi

    log_success &quot;활성 환경: $ACTIVE_ENV (포트: $ACTIVE_PORT)&quot;
    log_info &quot;배포 대상 환경: $INACTIVE_ENV (포트: $INACTIVE_PORT)&quot;
}

# Docker 이미지 빌드
build_image() {
    log_info &quot;Docker 이미지 빌드 중...&quot;

    IMAGE_TAG=&quot;${IMAGE_TAG:-$(date +%Y%m%d-%H%M%S)}&quot;
    export IMAGE_TAG

    docker compose -f $DOCKER_COMPOSE_FILE build test-$INACTIVE_ENV

    log_success &quot;이미지 빌드 완료: test:$IMAGE_TAG&quot;
}

# 비활성 환경에 새 버전 배포
deploy_to_inactive() {
    log_info &quot;$INACTIVE_ENV 환경에 배포 중...&quot;

    # 기존 컨테이너 중지 및 제거
    docker compose -f $DOCKER_COMPOSE_FILE stop test-$INACTIVE_ENV || true
    docker compose -f $DOCKER_COMPOSE_FILE rm -f test-$INACTIVE_ENV || true

    # 새 컨테이너 시작
    docker compose -f $DOCKER_COMPOSE_FILE up -d test-$INACTIVE_ENV

    log_success &quot;$INACTIVE_ENV 환경 시작 완료&quot;
}

# 헬스체크
health_check() {
    log_info &quot;$INACTIVE_ENV 환경 헬스체크 중...&quot;

    MAX_RETRY=30
    RETRY_COUNT=0
    HEALTH_URL=&quot;http://localhost:$INACTIVE_PORT/actuator/health&quot;

    while [ $RETRY_COUNT -lt $MAX_RETRY ]; do
        if curl -f -s &quot;$HEALTH_URL&quot; &amp;gt; /dev/null 2&amp;gt;&amp;amp;1; then
            log_success &quot;헬스체크 성공&quot;
            return 0
        fi

        RETRY_COUNT=$((RETRY_COUNT + 1))
        log_warning &quot;헬스체크 대기 중... ($RETRY_COUNT/$MAX_RETRY)&quot;
        sleep 10
    done

    log_error &quot;헬스체크 실패: $INACTIVE_ENV 환경이 정상적으로 시작되지 않았습니다.&quot;
    exit 1
}

# ALB 타겟 그룹 등록 확인
check_target_health() {
    log_info &quot;ALB 타겟 헬스 상태 확인 중...&quot;

	# 배포할 환경의 타겟 그룹 ARN 결정
    if [ &quot;$INACTIVE_ENV&quot; = &quot;blue&quot; ]; then
        TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_BLUE_ARN&quot;
    else
        TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_GREEN_ARN&quot;
    fi

    # EC2 인스턴스 ID 가져오기(IMDSv2 우선, v1 폴백)
    # 토큰 발급(IMDSv2)
    TOKEN=$(curl -X PUT &quot;http://169.254.169.254/latest/api/token&quot; \
        -H &quot;X-aws-ec2-metadata-token-ttl-seconds: 21600&quot; \
        -s --connect-timeout 2)

    # 사용하여 인스턴스 ID 가져오기
    if [ -n &quot;$TOKEN&quot; ]; then
        # IMDSv2 사용
        INSTANCE_ID=$(curl -H &quot;X-aws-ec2-metadata-token: $TOKEN&quot; \
            -s http://169.254.169.254/latest/meta-data/instance-id)
        log_info &quot;IMDSv2를 사용하여 인스턴스 ID 가져옴&quot;
    else
        # IMDSv1 fallback
        INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id \
            --connect-timeout 2)
        log_info &quot;IMDSv1을 사용하여 인스턴스 ID 가져옴&quot;
    fi


    # 디버깅: 변수 값 확인
    log_info &quot;인스턴스 ID: $INSTANCE_ID&quot;
    log_info &quot;타겟 그룹 ARN: $TARGET_GROUP_ARN&quot;
    log_info &quot;포트: $INACTIVE_PORT&quot;

    # 변수 검증
    if [ -z &quot;$INSTANCE_ID&quot; ] || [ &quot;$INSTANCE_ID&quot; = &quot;null&quot; ]; then
        log_error &quot;인스턴스 ID를 가져올 수 없습니다.&quot;
        log_error &quot;IMDS가 활성화되어 있는지 확인하세요.&quot;
        exit 1
    fi
    
    if [ -z &quot;$INACTIVE_PORT&quot; ]; then
        log_error &quot;포트 번호가 설정되지 않았습니다.&quot;
        exit 1
    fi

      # 대기 시간 증가: 30번 &amp;times; 15초 = 최대 7.5분
      # 대기시간을 최대 7.5분으로 한 이유는 서버가 실행하고 대상그룹이 상태를 확인하는데 시간이 어느정도 소요되기 때문입니다.
      # 짧은 시간으로 설정하면 unhealthy로 인해 배포가 실패합니다.
      MAX_RETRY=30
      RETRY_COUNT=0
      SLEEP_INTERVAL=15

    log_info &quot;타겟 헬스 체크 시작 (최대 대기: $((MAX_RETRY * SLEEP_INTERVAL))초)&quot;

    while [ $RETRY_COUNT -lt $MAX_RETRY ]; do
        HEALTH_INFO=$(aws elbv2 describe-target-health \
            --target-group-arn &quot;$TARGET_GROUP_ARN&quot; \
            --targets Id=&quot;$INSTANCE_ID&quot;,Port=&quot;$INACTIVE_PORT&quot; \
            --query 'TargetHealthDescriptions[0].{State:TargetHealth.State,Reason:TargetHealth.Reason,Description:TargetHealth.Description}' \
            --output json 2&amp;gt;/dev/null)

        TARGET_HEALTH=$(echo &quot;$HEALTH_INFO&quot; | jq -r '.State // &quot;unknown&quot;')
        HEALTH_REASON=$(echo &quot;$HEALTH_INFO&quot; | jq -r '.Reason // &quot;&quot;')
        HEALTH_DESC=$(echo &quot;$HEALTH_INFO&quot; | jq -r '.Description // &quot;&quot;')


        if [ &quot;$TARGET_HEALTH&quot; = &quot;healthy&quot; ]; then
            log_success &quot;타겟 헬스 상태: healthy&quot;
            return 0
        fi

        RETRY_COUNT=$((RETRY_COUNT + 1))
         # 상세 정보 출력
        if [ -n &quot;$HEALTH_REASON&quot; ]; then
            log_warning &quot;타겟 헬스 대기 중... 상태: $TARGET_HEALTH, 사유: $HEALTH_REASON ($RETRY_COUNT/$MAX_RETRY)&quot;
        else
            log_warning &quot;타겟 헬스 대기 중... 현재 상태: $TARGET_HEALTH ($RETRY_COUNT/$MAX_RETRY)&quot;
        fi

        sleep $SLEEP_INTERVAL
    done


    log_error &quot;타겟이 healthy 상태가 되지 않았습니다.&quot;
    log_error &quot;마지막 상태: $TARGET_HEALTH&quot;
    if [ -n &quot;$HEALTH_REASON&quot; ]; then
        log_error &quot;사유: $HEALTH_REASON - $HEALTH_DESC&quot;
    fi
    exit 1
}

# ALB 리스너 규칙 변경 (트래픽 전환)
switch_traffic() {
    log_info &quot;트래픽을 $INACTIVE_ENV 환경으로 전환 중...&quot;

    if [ &quot;$INACTIVE_ENV&quot; = &quot;blue&quot; ]; then
        NEW_TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_BLUE_ARN&quot;
        OLD_TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_GREEN_ARN&quot;
    else
        NEW_TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_GREEN_ARN&quot;
        OLD_TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_BLUE_ARN&quot;
    fi

    # Weight로 타겟 그룹 변경
    # 새 타겟 그룹: Weight=1, 기존 타겟 그룹: Weight=0
    aws elbv2 modify-rule \
        --rule-arn &quot;$ALB_LISTENER_RULE_ARN&quot; \
        --actions Type=forward,ForwardConfig=&quot;{TargetGroups=[{TargetGroupArn=${OLD_TARGET_GROUP_ARN},Weight=0},{TargetGroupArn=${NEW_TARGET_GROUP_ARN},Weight=1}],TargetGroupStickinessConfig={Enabled=false}}&quot;

    log_success &quot;트래픽 전환 완료: $ACTIVE_ENV -&amp;gt; $INACTIVE_ENV&quot;
}

# 이전 환경 정리 (선택사항)
cleanup_old_environment() {
    log_info &quot;이전 환경 정리 여부 확인...&quot;

    # 30초 대기 후 이전 환경 중지 (롤백 가능 시간 확보)
    log_info &quot;30초 후 이전 환경($ACTIVE_ENV)을 중지합니다...&quot;
    sleep 30

    log_info &quot;$ACTIVE_ENV 환경 중지 중...&quot;
    docker compose -f $DOCKER_COMPOSE_FILE stop test-$ACTIVE_ENV

    log_success &quot;이전 환경 정리 완료&quot;
}

# 롤백 함수
rollback() {
    log_error &quot;배포 실패! 롤백을 시작합니다...&quot;

    # 트래픽을 다시 이전 환경으로 전환
    if [ &quot;$ACTIVE_ENV&quot; = &quot;blue&quot; ]; then
        ROLLBACK_TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_BLUE_ARN&quot;
        OTHER_TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_GREEN_ARN&quot;
    else
        ROLLBACK_TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_GREEN_ARN&quot;
        OTHER_TARGET_GROUP_ARN=&quot;$ALB_TARGET_GROUP_BLUE_ARN&quot;
    fi

    # Weight를 배포 인전 환경으로 되돌립니다.
    # 새 타겟 그룹: Weight=0, 기존 타겟 그룹: Weight=1
    aws elbv2 modify-rule \
        --rule-arn &quot;$ALB_LISTENER_RULE_ARN&quot; \
        --actions Type=forward,ForwardConfig=&quot;{TargetGroups=[{TargetGroupArn=${OTHER_TARGET_GROUP_ARN},Weight=0},{TargetGroupArn=${ROLLBACK_TARGET_GROUP_ARN},Weight=1}],TargetGroupStickinessConfig={Enabled=false}}&quot;

    # 실패한 컨테이너 중지
    docker compose -f $DOCKER_COMPOSE_FILE stop test-$INACTIVE_ENV

    log_success &quot;롤백 완료&quot;
    exit 1

}

# 메인 배포 프로세스
main() {
    log_info &quot;=========================================&quot;
    log_info &quot;Blue-Green 배포 시작&quot;
    log_info &quot;=========================================&quot;

    # 오류 발생 시 롤백
    trap rollback ERR

    # 사전 검증
	check_dependencies
    check_env_vars
    
    # 환경 분석
    get_active_environment

	# 새 버전 빌드 및 배포
	build_image
    deploy_to_inactive
	
    # 헬스체크
	health_check
    check_target_health

	# 트래픽 전환
    switch_traffic
    
    # 정리
    cleanup_old_environment

    log_success &quot;=========================================&quot;
    log_success &quot;배포 완료!&quot;
    log_success &quot;활성 환경: $INACTIVE_ENV (포트: $INACTIVE_PORT)&quot;
    log_success &quot;=========================================&quot;
}

# 스크립트 실행
main &quot;$@&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- deploy.sh이 실행이 되면서 EC2 내부에서 배포가 진행이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 각 함수마다 어떤 내용이 있는지 주석으로 설명을 작성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- check target health에서 7.5분까지 하지 않으셔도 되지만 health check가 실패한다면 시간을 너무 짧게 가져가셔 그럴 수 도 있으니 확인해보시면 될 거 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. docker-compose 파일 작성&lt;/p&gt;
&lt;pre id=&quot;code_1766929098748&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  # Blue 환경
  test-blue:
    build:
      context: .
      dockerfile: Dockerfile.deploy
      args:
        PROFILE: dev
    image: test:${IMAGE_TAG:-latest}
    container_name: test-blue
    env_file:
      - env/.env.dev
    environment:
      SPRING_PROFILES_ACTIVE: dev
      DB_HOST: ${DB_HOST}
      DB_PORT: ${DB_PORT}
      DB_NAME: ${DB_NAME}
      DB_SCHEMA: ${DB_SCHEMA}
      DB_USERNAME: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      SERVER_PORT: 8080
      TZ: Asia/Seoul
      DEPLOYMENT_ENV: blue
      JAVA_OPTS: &amp;gt;-
        -Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/test/heapdump.hprof -Duser.timezone=Asia/Seoul -Dfile.encoding=UTF-8
    ports:
      - &quot;8080:8080&quot;
    restart: always
    logging:
      driver: &quot;json-file&quot;
      options:
        max-size: &quot;100m&quot;
        max-file: &quot;10&quot;
    healthcheck:
      test: [ &quot;CMD&quot;, &quot;wget&quot;, &quot;--no-verbose&quot;, &quot;--tries=1&quot;, &quot;--spider&quot;, &quot;http://localhost:8080/actuator/health&quot; ]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 90s
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1536M
        reservations:
          cpus: '0.5'
          memory: 1024M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

  # Green 환경
  test-green:
    build:
      context: .
      dockerfile: Dockerfile.deploy
      args:
        PROFILE: dev
    image: test:${IMAGE_TAG:-latest}
    container_name: test-green
    env_file:
      - env/.env.dev
    environment:
      SPRING_PROFILES_ACTIVE: dev
      DB_HOST: ${DB_HOST}
      DB_PORT: ${DB_PORT}
      DB_NAME: ${DB_NAME}
      DB_SCHEMA: ${DB_SCHEMA}
      DB_USERNAME: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      SERVER_PORT: 8080
      TZ: Asia/Seoul
      DEPLOYMENT_ENV: green
      JAVA_OPTS: &amp;gt;-
        -Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/test/heapdump.hprof -Duser.timezone=Asia/Seoul -Dfile.encoding=UTF-8
    ports:
      - &quot;8081:8080&quot;
    restart: always
    logging:
      driver: &quot;json-file&quot;
      options:
        max-size: &quot;100m&quot;
        max-file: &quot;10&quot;
    healthcheck:
      test: [ &quot;CMD&quot;, &quot;wget&quot;, &quot;--no-verbose&quot;, &quot;--tries=1&quot;, &quot;--spider&quot;, &quot;http://localhost:8080/actuator/health&quot; ]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 90s
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1536M
        reservations:
          cpus: '0.5'
          memory: 1024M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
....&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- docker compose 파일은 blue, green 서비스를 생성하는 방식으로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- script에서도 실행을 할 때, blue가 active 상태인 경우 green을 실행하도록 구성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-28 오후 10.32.51.png&quot; data-origin-width=&quot;2434&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3PuYV/dJMcagD9XAl/VFH02upsaXRJoeRsDH0Skk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3PuYV/dJMcagD9XAl/VFH02upsaXRJoeRsDH0Skk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3PuYV/dJMcagD9XAl/VFH02upsaXRJoeRsDH0Skk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3PuYV%2FdJMcagD9XAl%2FVFH02upsaXRJoeRsDH0Skk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2434&quot; height=&quot;508&quot; data-filename=&quot;스크린샷 2025-12-28 오후 10.32.51.png&quot; data-origin-width=&quot;2434&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 정상적으로 배포가 완료되었다면 CodeDeploy에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 github-action에서 마지막 step 이후에 배포가 성공/실패/취소 여부에 따라서 슬랙에 알림이 오도록 구현하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 CI/CD와는 연결된 부분이 아니라서 제거하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CI/CD를 할 때 여러 번 실패를 경험하고 조금씩 수정하면서 진행하다 보니 생각보다 걸리긴 했지만, 저도 여러번 실패학 수정하고 시도하였습니다. 그래도 성공했을 때 행복했던 느낌을 같이 가져가셨으면 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://develop-706.tistory.com/40&quot;&gt;https://develop-706.tistory.com/40&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766922943862&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[CI/CD] Github Action 을 통한 Spring boot 배포 자동화 구축하기 !&quot; data-og-description=&quot;  개요배포 경험이 있는 사람들은 공감하실 것 입니다.수동으로 배포하는 과정이 얼마나 힘들고 귀찮은 과정인지를..!&amp;nbsp;&amp;nbsp;저번에는 Github 과 Jenkins 를 사용해서 배포 자동화를 구축했었는데요.&amp;nbsp;&quot; data-og-host=&quot;develop-706.tistory.com&quot; data-og-source-url=&quot;https://develop-706.tistory.com/40&quot; data-og-url=&quot;https://develop-706.tistory.com/40&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/40szw/hyZQAfMgPE/uFBekyliVQyRirpm0rWB7k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/il9so/hyZPFWcBPG/H6hRyczhVfTd33BE0EZMSk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/DWSL6/hyZQE3xNL6/oKgDkznEQP6ozGc3VFhxd1/img.png?width=1430&amp;amp;height=687&amp;amp;face=0_0_1430_687&quot;&gt;&lt;a href=&quot;https://develop-706.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://develop-706.tistory.com/40&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/40szw/hyZQAfMgPE/uFBekyliVQyRirpm0rWB7k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/il9so/hyZPFWcBPG/H6hRyczhVfTd33BE0EZMSk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/DWSL6/hyZQE3xNL6/oKgDkznEQP6ozGc3VFhxd1/img.png?width=1430&amp;amp;height=687&amp;amp;face=0_0_1430_687');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[CI/CD] Github Action 을 통한 Spring boot 배포 자동화 구축하기 !&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  개요배포 경험이 있는 사람들은 공감하실 것 입니다.수동으로 배포하는 과정이 얼마나 힘들고 귀찮은 과정인지를..!&amp;nbsp;&amp;nbsp;저번에는 Github 과 Jenkins 를 사용해서 배포 자동화를 구축했었는데요.&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;develop-706.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot;&gt;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766922949596&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring Boot + GitHub Actions + AWS CodeDeploy를 사용하여 CI/CD 구축하기&quot; data-og-description=&quot;Spring Boot + GitHub Actions + AWS CodeDeploy를 사용하여 CI/CD 구축하기&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@chae_ag/series배포Spring-Boot-GitHub-Actions-AWS-CodeDeploy를-사용하여-CICD-구축하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkpLjg/hyZPIrPPln/M0t5Uck9laBUHsV8n3QnC1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/bndYfV/hyZPF2Wvkt/IEa4KfHcLciV16KJA74Zm0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/gZ02e/hyZQrJUz8s/njEirAm1yUaZ6CJYQfPLfK/img.png?width=1580&amp;amp;height=739&amp;amp;face=0_0_1580_739&quot;&gt;&lt;a href=&quot;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkpLjg/hyZPIrPPln/M0t5Uck9laBUHsV8n3QnC1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/bndYfV/hyZPF2Wvkt/IEa4KfHcLciV16KJA74Zm0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/gZ02e/hyZQrJUz8s/njEirAm1yUaZ6CJYQfPLfK/img.png?width=1580&amp;amp;height=739&amp;amp;face=0_0_1580_739');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot + GitHub Actions + AWS CodeDeploy를 사용하여 CI/CD 구축하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot + GitHub Actions + AWS CodeDeploy를 사용하여 CI/CD 구축하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/AWS</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/438</guid>
      <comments>https://baam.tistory.com/438#entry438comment</comments>
      <pubDate>Sun, 28 Dec 2025 22:46:30 +0900</pubDate>
    </item>
    <item>
      <title>[EC2] Github-Action으로 EC2에 Spring-Boot 배포(Blue-Green) - 1</title>
      <link>https://baam.tistory.com/437</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring-Boot 서버를 EC2에 수동으로 배포를 하려면 code를 merge 한 이후에 인스턴스에서 pull 받은 후 컨테이너를 다시 재실행해야 했습니다. 이 방식의 문제점은 수동 배포를 하는 과정은 물론 서버의 중단이 발생하며, 컨테이너가 build 후 재실행하면서 어떠한 요청에도 응답을 줄 수 없는 상태가 됩니다. 사용자 및 운영적인 측면에서 큰 문제가 될 수 있다고 판단하여, CI/CD를 적용하고 서버도 Blue-Green방식으로 변경하는 과정을 경험하였고 그 과정을 작성하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;혹시라도 부정확한 정보를 전달드릴 수 있습니다. 다만 틀린 거나 부정확한 정보가 있다면 댓글을 남겨주세요&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 저는 SSM Parameter를 이용하고 있습니다. &lt;br /&gt;* Github-Action의 Secerts을 일부분 사용하고 있으며, env는 사용하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 혹시 Github-Action의 env를 이용하신다면 그부분은 github workflow에서 수정해서 사용하시면 될 거 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. IAM 생성 및 EC2 인스턴스에 IAM 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.15.59.png&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;852&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEO2Sz/dJMcabv2UVL/MKrkCJOtfr9JDtslnV9cXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEO2Sz/dJMcabv2UVL/MKrkCJOtfr9JDtslnV9cXK/img.png&quot; data-alt=&quot;IAM 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEO2Sz/dJMcabv2UVL/MKrkCJOtfr9JDtslnV9cXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEO2Sz%2FdJMcabv2UVL%2FMKrkCJOtfr9JDtslnV9cXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;311&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.15.59.png&quot; data-origin-width=&quot;1432&quot; data-origin-height=&quot;852&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IAM 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.16.17.png&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;770&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9eqeQ/dJMcaaw8BiS/qKHdxmWax7dEKpWP9cnja1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9eqeQ/dJMcaaw8BiS/qKHdxmWax7dEKpWP9cnja1/img.png&quot; data-alt=&quot;IAM 생성&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9eqeQ/dJMcaaw8BiS/qKHdxmWax7dEKpWP9cnja1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9eqeQ%2FdJMcaaw8BiS%2FqKHdxmWax7dEKpWP9cnja1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;392&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.16.17.png&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;770&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IAM 생성&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- IAM에서 역할 생성을 선택 후 사용사례에서 EC2를 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t1SPR/dJMcadm2gA5/z77ceFlKwoBjoDM2T1qIRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t1SPR/dJMcadm2gA5/z77ceFlKwoBjoDM2T1qIRk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;848&quot; data-origin-height=&quot;584&quot; data-filename=&quot;blob&quot; width=&quot;553&quot; height=&quot;479&quot; data-widthpercent=&quot;60.55&quot; style=&quot;width: 59.8472%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t1SPR/dJMcadm2gA5/z77ceFlKwoBjoDM2T1qIRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft1SPR%2FdJMcadm2gA5%2Fz77ceFlKwoBjoDM2T1qIRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;848&quot; height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uFY8z/dJMcaion4xM/hjAA3HfmcNn1asBybtjVVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uFY8z/dJMcaion4xM/hjAA3HfmcNn1asBybtjVVk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;926&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.33.10.png&quot; style=&quot;width: 38.99%;&quot; data-widthpercent=&quot;39.45&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uFY8z/dJMcaion4xM/hjAA3HfmcNn1asBybtjVVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuFY8z%2FdJMcaion4xM%2FhjAA3HfmcNn1asBybtjVVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;876&quot; height=&quot;926&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;IAM 권한추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AmazonS3ReadOnlyAccess, AmazonSSMReadOnlyAccess, AWSCodeDeployRole&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 4개의 권한을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - AmazonS3ReadOnlyAccess: &lt;span&gt;CodeDeploy가 S3에서 배포 파일을 &lt;/span&gt;&lt;span&gt; 가져오기 위함입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - AmazonSSMReadOnlyAccess: ssm parameter에 있는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;env파일을&lt;span&gt;&amp;nbsp;가져오기 위함입니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; - AWSCodeDeployRole: AWS CodeDeploy agent가 &lt;span&gt;EC2에서 &lt;/span&gt;&lt;span&gt; 배포 작업을 수행하기 위함입니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;- 이름을 입력하여 IAM을 생성합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;- 오른쪽 이미지 처럼 권한정책을 확인할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1766659477941&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
	&quot;Version&quot;: &quot;2012-10-17&quot;,
	&quot;Statement&quot;: [
		{   // Blue-Green 배포를 위해 리스너 규칙을 수정하고, 대상 그룹 상태를 확인하는 권한입니다.
			&quot;Effect&quot;: &quot;Allow&quot;,
			&quot;Action&quot;: [
				&quot;elasticloadbalancing:ModifyListener&quot;,
				&quot;elasticloadbalancing:DescribeListeners&quot;,
				&quot;elasticloadbalancing:DescribeTargetHealth&quot;,
				&quot;elasticloadbalancing:DescribeTargetGroups&quot;
			],
			&quot;Resource&quot;: &quot;*&quot;
		},
		{
        	// CodeDeploy가 배포 대상 EC2 인스턴스 정보를 조회하기 위한 권한입니다.
			&quot;Effect&quot;: &quot;Allow&quot;,
			&quot;Action&quot;: [
				&quot;ec2:DescribeInstances&quot;
			],
			&quot;Resource&quot;: &quot;*&quot;
		},
		{
        	// SSM Parameter 환경변수를 읽어오기 위한 권한입니다.
			&quot;Effect&quot;: &quot;Allow&quot;,
			&quot;Action&quot;: [
				&quot;ssm:GetParameter&quot;,
				&quot;ssm:GetParameters&quot;,
				&quot;ssm:GetParametersByPath&quot;
			],
			&quot;Resource&quot;: [
    			// 파라미터에서 지정한 경로를 등록합니다.
				&quot;arn:aws:ssm:ap-northeast-2:*:parameter/test/dev/*&quot;
			]
		},
		{
        	// ssm에서 유형이 SecureString인 경우 설정하셔야 합니다.
	        // SSM SecureString 파라미터 복호화를 위한 KMS 권한입니다.
			&quot;Effect&quot;: &quot;Allow&quot;,
			&quot;Action&quot;: [
				&quot;kms:Decrypt&quot;,
				&quot;kms:DescribeKey&quot;
			],
			&quot;Resource&quot;: &quot;*&quot;,
			&quot;Condition&quot;: {
				&quot;StringEquals&quot;: {
					&quot;kms:ViaService&quot;: &quot;ssm.ap-northeast-2.amazonaws.com&quot;
				}
			}
		},
		{
        	// CodeDeploy가 S3에서 배포 파일을 다운로드하기 위한 권한입니다.
			&quot;Effect&quot;: &quot;Allow&quot;,
			&quot;Action&quot;: [
				&quot;s3:GetObject&quot;,
				&quot;s3:ListBucket&quot;
			],
			&quot;Resource&quot;: [
				&quot;arn:aws:s3:::test-deployments&quot;,
		        &quot;arn:aws:s3:::test-deployments/*&quot;
			]
		}
	]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 추가적으로 권한을 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.46.19.png&quot; data-origin-width=&quot;1738&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uIzlh/dJMcabv2VOr/zKiGhHP4n3mOfDNMFMiWe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uIzlh/dJMcabv2VOr/zKiGhHP4n3mOfDNMFMiWe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uIzlh/dJMcabv2VOr/zKiGhHP4n3mOfDNMFMiWe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuIzlh%2FdJMcabv2VOr%2FzKiGhHP4n3mOfDNMFMiWe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;259&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.46.19.png&quot; data-origin-width=&quot;1738&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- AWSCodeDeployRole 정책도 추가로 생성합니다.(CodeDeploy에서 사용할 예정입니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.59.25.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z3bIc/dJMcabiv9I0/82QiyYtY50ri1quzo8Si0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z3bIc/dJMcabiv9I0/82QiyYtY50ri1quzo8Si0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z3bIc/dJMcabiv9I0/82QiyYtY50ri1quzo8Si0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ3bIc%2FdJMcabiv9I0%2F82QiyYtY50ri1quzo8Si0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;352&quot; height=&quot;317&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.59.25.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 최종적으로 2개의 역할을 생성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. SSM Parameter Store에 Env 경로 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.47.59.png&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOTIXB/dJMcagYpGPR/bnGUO3kdXXkEydFFok5VS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOTIXB/dJMcagYpGPR/bnGUO3kdXXkEydFFok5VS0/img.png&quot; data-alt=&quot;ssm parameter store&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOTIXB/dJMcagYpGPR/bnGUO3kdXXkEydFFok5VS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOTIXB%2FdJMcagYpGPR%2FbnGUO3kdXXkEydFFok5VS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;142&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.47.59.png&quot; data-origin-width=&quot;1412&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ssm parameter store&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.51.01.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;1216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cohCAI/dJMcai9IY85/b7K1gTAAMowpMUIaYQXqi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cohCAI/dJMcai9IY85/b7K1gTAAMowpMUIaYQXqi1/img.png&quot; data-alt=&quot;parameter store 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cohCAI/dJMcai9IY85/b7K1gTAAMowpMUIaYQXqi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcohCAI%2FdJMcai9IY85%2Fb7K1gTAAMowpMUIaYQXqi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;399&quot; data-filename=&quot;스크린샷 2025-12-25 오후 7.51.01.png&quot; data-origin-width=&quot;1238&quot; data-origin-height=&quot;1216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;parameter store 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파라미터 생성을 선택 후 이름, 설명(optional), 유형을 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이름은 IAM에 등록한 경로로 해줘야 읽을 수 있습니다.(ex. /test/dev/...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 유형에서 보안 문자열(SecureString)을 사용하시지 않는다면 IAM에서 설정한 부분을 제거해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터 형식에는 로컬에서 사용하던 env 내용을 복사해서 넣어주면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. VPC Endpoint 생성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.05.45.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1C0DU/dJMcagREmae/MP4BsF2bKXCZrKgodSKgdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1C0DU/dJMcagREmae/MP4BsF2bKXCZrKgodSKgdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1C0DU/dJMcagREmae/MP4BsF2bKXCZrKgodSKgdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1C0DU%2FdJMcagREmae%2FMP4BsF2bKXCZrKgodSKgdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;443&quot; height=&quot;67&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.05.45.png&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.06.32.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mlzR1/dJMcadUSyLH/mNttEj80njFZdkU7yzbQRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mlzR1/dJMcadUSyLH/mNttEj80njFZdkU7yzbQRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mlzR1/dJMcadUSyLH/mNttEj80njFZdkU7yzbQRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmlzR1%2FdJMcadUSyLH%2FmNttEj80njFZdkU7yzbQRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;120&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.06.32.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.07.06.png&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;386&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cINqyx/dJMcaaKFwQ9/JJkkwhDFJXhR3Ow4iDfb2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cINqyx/dJMcaaKFwQ9/JJkkwhDFJXhR3Ow4iDfb2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cINqyx/dJMcaaKFwQ9/JJkkwhDFJXhR3Ow4iDfb2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcINqyx%2FdJMcaaKFwQ9%2FJJkkwhDFJXhR3Ow4iDfb2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;481&quot; height=&quot;114&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.07.06.png&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;386&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.11.07.png&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xpCCT/dJMcaaKFw0t/TuUaiYebFKQVb9kTU6ES0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xpCCT/dJMcaaKFw0t/TuUaiYebFKQVb9kTU6ES0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xpCCT/dJMcaaKFw0t/TuUaiYebFKQVb9kTU6ES0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxpCCT%2FdJMcaaKFw0t%2FTuUaiYebFKQVb9kTU6ES0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;424&quot; height=&quot;226&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.11.07.png&quot; data-origin-width=&quot;1012&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.13.55.png&quot; data-origin-width=&quot;1592&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckpfu5/dJMcadHmd0b/F6Z2r6zcHz18X2p6E9l5h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckpfu5/dJMcadHmd0b/F6Z2r6zcHz18X2p6E9l5h0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckpfu5/dJMcadHmd0b/F6Z2r6zcHz18X2p6E9l5h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckpfu5%2FdJMcadHmd0b%2FF6Z2r6zcHz18X2p6E9l5h0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;575&quot; height=&quot;182&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.13.55.png&quot; data-origin-width=&quot;1592&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- NAT Gateway가 없으면 Private subnet으로 서버를 운영할 때 가져올 수 있는 방법이 없어서, AWS에 접근하기 위해서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; - SSM Endpoint: Parameter Store에서 환경변수 파일을 가져옵니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; - S3 Endpoint: CodeDeploy가 배포 파일을 다운로드합니다.(Gateway)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- EC2 인스턴스가 생성된 VPC로 지정해주시면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- SSM 지정 시 서브넷이 나오는데 EC2가 위치해 있는 서브넷을 지정해 주시면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; - 보안그룹을 지정하는 부분에서는 인바운드 규칙에서 소스에 EC2 인스턴스의 보안그룹 id를 입력합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. ALB에 대상그룹 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Blue-Green 배포를 위해서는 기존에 하나의 대상그룹에서 추가적으로 다른 포트로 대상그룹을 생성해 주고 연결해줘야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/roXf0/dJMcaiPqxpV/tnjd7I9GjgTpEh5Ohy4MeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/roXf0/dJMcaiPqxpV/tnjd7I9GjgTpEh5Ohy4MeK/img.png&quot; width=&quot;504&quot; height=&quot;375&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;1252&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.25.35.png&quot; style=&quot;width: 49.1983%; margin-right: 10px;&quot; data-widthpercent=&quot;49.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/roXf0/dJMcaiPqxpV/tnjd7I9GjgTpEh5Ohy4MeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FroXf0%2FdJMcaiPqxpV%2Ftnjd7I9GjgTpEh5Ohy4MeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1682&quot; height=&quot;1252&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r7ap6/dJMcahpvkUM/4foxOfxvO3YtbswW57tPoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r7ap6/dJMcahpvkUM/4foxOfxvO3YtbswW57tPoK/img.png&quot; data-origin-width=&quot;1632&quot; data-origin-height=&quot;1204&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.6389%;&quot; data-widthpercent=&quot;50.22&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r7ap6/dJMcahpvkUM/4foxOfxvO3YtbswW57tPoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr7ap6%2FdJMcahpvkUM%2F4foxOfxvO3YtbswW57tPoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1632&quot; height=&quot;1204&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 저는 포트를 8080과 8085를 사용하고 있어서 2개의 대상그룹으로 생성하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- VPC도 인스턴스가 있는 VPC로 선택합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 상태검사는 코드에서 구현하신 부분을 작성하면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1928&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVLmBf/dJMcac2KoQT/PHGzHRebuMW7vLePy8G2y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVLmBf/dJMcac2KoQT/PHGzHRebuMW7vLePy8G2y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVLmBf/dJMcac2KoQT/PHGzHRebuMW7vLePy8G2y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVLmBf%2FdJMcac2KoQT%2FPHGzHRebuMW7vLePy8G2y0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1928&quot; height=&quot;712&quot; data-origin-width=&quot;1928&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 생성한 대상 그룹을 ALB에 연결합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 이때 가중치는 현재 실행되고 있는 서버의 포트와 일치하는 대상그룹에 100%을 지정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 대상그룹의 변경은 CodeDeploy에서 실행이 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- ALB의 보안그룹에서 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;아웃바운드&lt;/span&gt;&lt;/b&gt;도 설정한 포트를 사용할 수 있도록 해줘야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;5. S3에 버킷 생성&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 배포 파일이 저장될 S3 버킷을 생성합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 버킷 명은 이전에 IAM에서 입력한 버킷명과 일치해야 합니다.(ex. test-deployments)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;6. EC2 - IAM을 역할 지정&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;- 이전에 생성한 IAM을 지정합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;7. CodeDeploy - 애플리케이션 생성&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.55.50.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;938&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QDJS1/dJMcahXlwu9/PWKve2CabBEarFgNByFWAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QDJS1/dJMcahXlwu9/PWKve2CabBEarFgNByFWAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QDJS1/dJMcahXlwu9/PWKve2CabBEarFgNByFWAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQDJS1%2FdJMcahXlwu9%2FPWKve2CabBEarFgNByFWAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;360&quot; height=&quot;277&quot; data-filename=&quot;스크린샷 2025-12-25 오후 8.55.50.png&quot; data-origin-width=&quot;1220&quot; data-origin-height=&quot;938&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 애플리케이션을 생성한 후 그룹도 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 9.01.13.png&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;1212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ePeQXq/dJMcacuXbXn/2DUwReLxEDBXUphJQzvNcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ePeQXq/dJMcacuXbXn/2DUwReLxEDBXUphJQzvNcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ePeQXq/dJMcacuXbXn/2DUwReLxEDBXUphJQzvNcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FePeQXq%2FdJMcacuXbXn%2F2DUwReLxEDBXUphJQzvNcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;374&quot; data-filename=&quot;스크린샷 2025-12-25 오후 9.01.13.png&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;1212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 배포그룹 생성 시 IAM에서 생성한 AWSCodeDeployRole을 지정합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 9.02.25.png&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0iElf/dJMcaaqmSTb/yRDqeSNqE2xnymA8qlWDZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0iElf/dJMcaaqmSTb/yRDqeSNqE2xnymA8qlWDZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0iElf/dJMcaaqmSTb/yRDqeSNqE2xnymA8qlWDZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0iElf%2FdJMcaaqmSTb%2FyRDqeSNqE2xnymA8qlWDZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;424&quot; height=&quot;267&quot; data-filename=&quot;스크린샷 2025-12-25 오후 9.02.25.png&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;752&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저는 인스턴스 2개를 사용하지 않기 때문에 인스턴스의 이름으로 지정하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 키에 Name을 지정하면 인스턴스 목록이 나옵니다. 이후 원하는 인스턴스를 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-25 오후 9.03.53.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;1238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ud51s/dJMcaiu7Pyw/2KIt3DH5M6AeNnyiEzFK8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ud51s/dJMcaiu7Pyw/2KIt3DH5M6AeNnyiEzFK8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ud51s/dJMcaiu7Pyw/2KIt3DH5M6AeNnyiEzFK8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fud51s%2FdJMcaiu7Pyw%2F2KIt3DH5M6AeNnyiEzFK8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;419&quot; height=&quot;416&quot; data-filename=&quot;스크린샷 2025-12-25 오후 9.03.53.png&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;1238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 에이전트 구성은 인스턴스에 에이전트가 설치가 안 됐을 수 있습니다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// 인스턴스 내부에서 명령어 입력
sudo systemctl status codedeploy-agent&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/W6WZl/dJMcah35SVS/eWwHSu5U7eWh6nPxY3lZB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/W6WZl/dJMcah35SVS/eWwHSu5U7eWh6nPxY3lZB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/W6WZl/dJMcah35SVS/eWwHSu5U7eWh6nPxY3lZB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FW6WZl%2FdJMcah35SVS%2FeWwHSu5U7eWh6nPxY3lZB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1194&quot; height=&quot;90&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 설치가 됐다면 이미지처럼 active를 확인할 수 있습니다.(에이전트가 설치가 안 됐다면 수동으로 설치해야 합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 배포 설정은 AllAtOnce로 지정하였습니다. 단일 인스턴스에서 Blue-Green을 사용함으로 실패하더라도 실행 중인 서버는 유지되기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- AllAtOnce: 모든 인스턴스에 한 번에 배포&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- HalfAtATime: 다중 인스턴스의 경우 절반씩 배포&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- OneAtATime: 한 번에 하나씩만 배포&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CodeDeploy Script와 Github-Action 부분은 다음 글로 작성하도록 하겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://develop-706.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://develop-706.tistory.com/40&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766665400841&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[CI/CD] Github Action 을 통한 Spring boot 배포 자동화 구축하기 !&quot; data-og-description=&quot;  개요배포 경험이 있는 사람들은 공감하실 것 입니다.수동으로 배포하는 과정이 얼마나 힘들고 귀찮은 과정인지를..!&amp;nbsp;&amp;nbsp;저번에는 Github 과 Jenkins 를 사용해서 배포 자동화를 구축했었는데요.&amp;nbsp;&quot; data-og-host=&quot;develop-706.tistory.com&quot; data-og-source-url=&quot;https://develop-706.tistory.com/40&quot; data-og-url=&quot;https://develop-706.tistory.com/40&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dpH7nE/hyZPVZrJhE/IehkxQFHQRTdHBxR15vgm0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/rt0wf/hyZQtNOii7/qLLZoCZ35WWNtDzgpW4Av1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/KTEAt/hyZQCxdApm/THv8ziAQxkpheh6CZOD8Xk/img.png?width=1430&amp;amp;height=687&amp;amp;face=0_0_1430_687&quot;&gt;&lt;a href=&quot;https://develop-706.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://develop-706.tistory.com/40&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dpH7nE/hyZPVZrJhE/IehkxQFHQRTdHBxR15vgm0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/rt0wf/hyZQtNOii7/qLLZoCZ35WWNtDzgpW4Av1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/KTEAt/hyZQCxdApm/THv8ziAQxkpheh6CZOD8Xk/img.png?width=1430&amp;amp;height=687&amp;amp;face=0_0_1430_687');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[CI/CD] Github Action 을 통한 Spring boot 배포 자동화 구축하기 !&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  개요배포 경험이 있는 사람들은 공감하실 것 입니다.수동으로 배포하는 과정이 얼마나 힘들고 귀찮은 과정인지를..!&amp;nbsp;&amp;nbsp;저번에는 Github 과 Jenkins 를 사용해서 배포 자동화를 구축했었는데요.&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;develop-706.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766665406567&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Spring Boot + GitHub Actions + AWS CodeDeploy를 사용하여 CI/CD 구축하기&quot; data-og-description=&quot;Spring Boot + GitHub Actions + AWS CodeDeploy를 사용하여 CI/CD 구축하기&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@chae_ag/series배포Spring-Boot-GitHub-Actions-AWS-CodeDeploy를-사용하여-CICD-구축하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Rzebp/hyZQr3vIMh/fxbiUFUAvXBZwZGi58sZiK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/Yiyow/hyZP9wEin8/FwnEdXXr8A6Nlp8vv7bbak/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/gJMvg/hyZP43bAqa/uofgIUwlLC0IgKClu5mrI1/img.png?width=1920&amp;amp;height=895&amp;amp;face=0_0_1920_895&quot;&gt;&lt;a href=&quot;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@chae_ag/series%EB%B0%B0%ED%8F%ACSpring-Boot-GitHub-Actions-AWS-CodeDeploy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Rzebp/hyZQr3vIMh/fxbiUFUAvXBZwZGi58sZiK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/Yiyow/hyZP9wEin8/FwnEdXXr8A6Nlp8vv7bbak/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720,https://scrap.kakaocdn.net/dn/gJMvg/hyZP43bAqa/uofgIUwlLC0IgKClu5mrI1/img.png?width=1920&amp;amp;height=895&amp;amp;face=0_0_1920_895');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot + GitHub Actions + AWS CodeDeploy를 사용하여 CI/CD 구축하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot + GitHub Actions + AWS CodeDeploy를 사용하여 CI/CD 구축하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/AWS</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/437</guid>
      <comments>https://baam.tistory.com/437#entry437comment</comments>
      <pubDate>Thu, 25 Dec 2025 21:26:07 +0900</pubDate>
    </item>
    <item>
      <title>[Ngrok] Ngrok을 활용한 로컬 서버 전환</title>
      <link>https://baam.tistory.com/436</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;포트원의 웹훅을 활용하기 위해서 로컬에서 어떤 식으로 테스트할 수 있을지 찾아보게 되었습니다. 그중 Ngrok을 활용하면 테스트가 가능하다는 것을 확인하고 Ngrok의 가입부터 실행방법을 작성하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;혹시라도 부정확한 정보를 전달드릴 수 있습니다. 다만 틀린 거나 부정확한 정보가 있다면 댓글을 남겨주세요&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Ngrok 공식 사이트에서 환경에 맞게 설치 진행(&lt;a href=&quot;https://ngrok.com/download/mac-os&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사이트대로 따라하면 쉽게 설치할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkiK4M/dJMcadm04xE/YktLBo2eCzgMwRyKFhgroK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkiK4M/dJMcadm04xE/YktLBo2eCzgMwRyKFhgroK/img.png&quot; data-alt=&quot;Mac에서 Ngrok 설치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkiK4M/dJMcadm04xE/YktLBo2eCzgMwRyKFhgroK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkiK4M%2FdJMcadm04xE%2FYktLBo2eCzgMwRyKFhgroK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1502&quot; height=&quot;908&quot; data-origin-width=&quot;1502&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Mac에서 Ngrok 설치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 설치하면서 Ngrok에 회원가입을 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 진행하지 않으면 token을 발급받지 못하여 외부에서 접근을 할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 회원가입 후 Token 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 좌측 Your Authtoken을 클릭해서 토큰을 복사합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZO0XW/dJMb99Zg6hW/wBcmXX7Owgs3PNrTMxpb8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZO0XW/dJMb99Zg6hW/wBcmXX7Owgs3PNrTMxpb8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZO0XW/dJMb99Zg6hW/wBcmXX7Owgs3PNrTMxpb8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZO0XW%2FdJMb99Zg6hW%2FwBcmXX7Owgs3PNrTMxpb8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2160&quot; height=&quot;506&quot; data-origin-width=&quot;2160&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 로컬에 토큰을 등록합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;color: #1d1c1d; text-align: left;&quot;&gt;ngrok config add-authtoken '본인 토큰'&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 현재 로컬에서 실행중인 서버의 포트를 입력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;color: #1d1c1d; text-align: left;&quot;&gt;ngrok http '실행포트'&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1d1c1d; text-align: left;&quot;&gt;- 저는 8083으로 실행하고 있습니다.(ex. ngrok http 8083)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H2DB1/dJMcabbILlb/kltOBRmfjEgwd03HlONDnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H2DB1/dJMcabbILlb/kltOBRmfjEgwd03HlONDnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H2DB1/dJMcabbILlb/kltOBRmfjEgwd03HlONDnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH2DB1%2FdJMcabbILlb%2FkltOBRmfjEgwd03HlONDnk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;874&quot; height=&quot;377&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Forwarding에 URL이 있습니다. 해당 URL로 요청을 보내면 테스트를 진행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cordcat.tistory.com/105&quot;&gt;https://cordcat.tistory.com/105&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766411438175&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Ngrok - 특징 및 사용법 (로컬 서버를 공개 서버로)&quot; data-og-description=&quot;What is a NgrokNgrok은 로컬 개발 환경에서 인터넷을 통해 웹 애플리케이션에 안전하게 접근할 수 있도록 해주는 도구입니다. 보안 연결을 통해 인터넷에서 서버를 실행할 수 있으며, 웹 애플리케이&quot; data-og-host=&quot;cordcat.tistory.com&quot; data-og-source-url=&quot;https://cordcat.tistory.com/105&quot; data-og-url=&quot;https://cordcat.tistory.com/105&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ctZgbX/hyZP7dItnh/CViBzhbHvvdqRWTMvRV0e0/img.png?width=800&amp;amp;height=629&amp;amp;face=0_0_800_629,https://scrap.kakaocdn.net/dn/r29TH/hyZPPqezO0/BukA6F0wWAoKOHzDUuRsW0/img.png?width=800&amp;amp;height=629&amp;amp;face=0_0_800_629,https://scrap.kakaocdn.net/dn/cQ45FW/hyZPTzNazR/hOeXgxUd4TC4KalU7oOsKk/img.png?width=1092&amp;amp;height=859&amp;amp;face=0_0_1092_859&quot;&gt;&lt;a href=&quot;https://cordcat.tistory.com/105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cordcat.tistory.com/105&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ctZgbX/hyZP7dItnh/CViBzhbHvvdqRWTMvRV0e0/img.png?width=800&amp;amp;height=629&amp;amp;face=0_0_800_629,https://scrap.kakaocdn.net/dn/r29TH/hyZPPqezO0/BukA6F0wWAoKOHzDUuRsW0/img.png?width=800&amp;amp;height=629&amp;amp;face=0_0_800_629,https://scrap.kakaocdn.net/dn/cQ45FW/hyZPTzNazR/hOeXgxUd4TC4KalU7oOsKk/img.png?width=1092&amp;amp;height=859&amp;amp;face=0_0_1092_859');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Ngrok - 특징 및 사용법 (로컬 서버를 공개 서버로)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What is a NgrokNgrok은 로컬 개발 환경에서 인터넷을 통해 웹 애플리케이션에 안전하게 접근할 수 있도록 해주는 도구입니다. 보안 연결을 통해 인터넷에서 서버를 실행할 수 있으며, 웹 애플리케이&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cordcat.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/배포</category>
      <category>localhost</category>
      <category>ngrok</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/436</guid>
      <comments>https://baam.tistory.com/436#entry436comment</comments>
      <pubDate>Mon, 22 Dec 2025 23:25:16 +0900</pubDate>
    </item>
    <item>
      <title>[AWS]No space left on device @ fptr_finalize_flush - /opt/codedeploy-agent/deployment-root/ongoing-deployment/</title>
      <link>https://baam.tistory.com/435</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AWS Ec2를 이용 중인 상황에서 CodeDeploy로 배포를 진행하고 있습니다. 그러다 마주친 에러가 있어서 글을 남겨보려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;혹시라도 부정확한 정보를 전달드릴 수 있습니다. 다만 틀린거나 부정확한 정보가 있다면 댓글을 남겨주세요&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;No space left on device&lt;/b&gt;는 에러에서 확인할 수 있듯이 디바이스에 남는 공간이 없다는 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 CodeDeploy를 이용해서 배포를 하려고 했으나 여유 공간이 없다는 것을 확인하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 에러 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-22 오후 12.12.08.png&quot; data-origin-width=&quot;965&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bflxXp/dJMcahXkf8f/c3dPEt1tr1mwaMB2JCQL6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bflxXp/dJMcahXkf8f/c3dPEt1tr1mwaMB2JCQL6k/img.png&quot; data-alt=&quot;CodeDeploy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bflxXp/dJMcahXkf8f/c3dPEt1tr1mwaMB2JCQL6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbflxXp%2FdJMcahXkf8f%2Fc3dPEt1tr1mwaMB2JCQL6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;965&quot; height=&quot;363&quot; data-filename=&quot;스크린샷 2025-12-22 오후 12.12.08.png&quot; data-origin-width=&quot;965&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CodeDeploy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 배포 실패시 이벤트에서 메시지를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;No space left on device 에러 확인&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 인스턴스의 남은 볼륨 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 인스턴스에 접근하여 &lt;b&gt;df -h&lt;/b&gt; 로 확인이 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-12-22 오후 12.12.26.png&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;145&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d8qKPj/dJMcahCZZg7/WqodCdkqDSkcdBrEYQXiAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d8qKPj/dJMcahCZZg7/WqodCdkqDSkcdBrEYQXiAK/img.png&quot; data-alt=&quot;Ec2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d8qKPj/dJMcahCZZg7/WqodCdkqDSkcdBrEYQXiAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd8qKPj%2FdJMcahCZZg7%2FWqodCdkqDSkcdBrEYQXiAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;476&quot; height=&quot;145&quot; data-filename=&quot;스크린샷 2025-12-22 오후 12.12.26.png&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;145&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ec2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실제로 확인하니 /dev/root의 size가 15G가이면 15G를 모두 사용중입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 인스턴스 생성시 볼륨을 30G까지는 무료이나 해당 인스턴스는 15G로 생성된 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 30G로 볼륨을 확장하는 방법은 GUI에서 볼륨을 선택하여 추가적으로 증가시킬 수 있습니다.(볼륨 증가관련 부분 제가 참고한 블로그를 확인하시면 쉽게 따라하실 수 있습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 불필요한 파일 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저의 경우는 확장은 하지않고 어떤 파일들이 문제를 발생하는지 알아서 삭제 처리하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 불필요한 파일들은 CI/CD를 진행하면서 기존에 배포했던 폴더를 zip파일로 backup 폴더에서 관리하도록 하면서 발생한 문제였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@yul_ee/AWS-CodeDeploy-%EB%B0%B0%ED%8F%AC%EC%8B%A4%ED%8C%A8-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot;&gt;https://velog.io/@yul_ee/AWS-CodeDeploy-%EB%B0%B0%ED%8F%AC%EC%8B%A4%ED%8C%A8-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766409463592&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[AWS] 서버 용량 부족으로 인한 CodeDeploy 배포실패 해결하기 &amp;zwj; &quot; data-og-description=&quot;문제 발견 '프리비' 서비스를 개발하는 과정에서 얼마전부터 계속 CodeDeploy를 이용한 배포가 실패하는 문제가 발생했다. 꽤 오래전부터 배포 실패가 계속되고 있었는데, 프론트엔드와 백엔드의 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@yul_ee/AWS-CodeDeploy-%EB%B0%B0%ED%8F%AC%EC%8B%A4%ED%8C%A8-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@yul_ee/AWS-CodeDeploy-배포실패-문제-해결하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/s1cHx/hyZPFOE3yt/DKWpkhedfrUk9sQ2L6xHZk/img.png?width=1532&amp;amp;height=728&amp;amp;face=0_0_1532_728,https://scrap.kakaocdn.net/dn/r4flZ/hyZP1q2UgC/mux5SQ8O46l5k6nz5TkdXk/img.png?width=1532&amp;amp;height=728&amp;amp;face=0_0_1532_728,https://scrap.kakaocdn.net/dn/JmYVP/hyZPZ03RGq/jIzb67Eh0aVW3VK6NkynG0/img.png?width=2000&amp;amp;height=1316&amp;amp;face=0_0_2000_1316&quot;&gt;&lt;a href=&quot;https://velog.io/@yul_ee/AWS-CodeDeploy-%EB%B0%B0%ED%8F%AC%EC%8B%A4%ED%8C%A8-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@yul_ee/AWS-CodeDeploy-%EB%B0%B0%ED%8F%AC%EC%8B%A4%ED%8C%A8-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/s1cHx/hyZPFOE3yt/DKWpkhedfrUk9sQ2L6xHZk/img.png?width=1532&amp;amp;height=728&amp;amp;face=0_0_1532_728,https://scrap.kakaocdn.net/dn/r4flZ/hyZP1q2UgC/mux5SQ8O46l5k6nz5TkdXk/img.png?width=1532&amp;amp;height=728&amp;amp;face=0_0_1532_728,https://scrap.kakaocdn.net/dn/JmYVP/hyZPZ03RGq/jIzb67Eh0aVW3VK6NkynG0/img.png?width=2000&amp;amp;height=1316&amp;amp;face=0_0_2000_1316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[AWS] 서버 용량 부족으로 인한 CodeDeploy 배포실패 해결하기 &amp;zwj; &lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제 발견 '프리비' 서비스를 개발하는 과정에서 얼마전부터 계속 CodeDeploy를 이용한 배포가 실패하는 문제가 발생했다. 꽤 오래전부터 배포 실패가 계속되고 있었는데, 프론트엔드와 백엔드의&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/AWS</category>
      <category>AWS</category>
      <category>codedeploy</category>
      <category>EC2</category>
      <category>Spring-boot</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/435</guid>
      <comments>https://baam.tistory.com/435#entry435comment</comments>
      <pubDate>Mon, 22 Dec 2025 22:39:35 +0900</pubDate>
    </item>
    <item>
      <title>ACM(외부에서 발급받은 도메인) - Route53 연결</title>
      <link>https://baam.tistory.com/434</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AWS에서 발급받은 도메인이 아닌 경우 자동으로 갱신이 되지 않고 발급받은 도메인을 직접 등록해줘야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 현재 회사에서 제가 도메인을 직접 발급받지 않았지만 ALB와 연결을 하기 위해서 ACM을 설정해 주는 방법을 기록하고 작성하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;혹시라도 부정확한 정보를 전달드릴 수 있습니다. 다만 틀린거나 부정확한 정보가 있다면 댓글을 남겨주세요&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. AWS ACM에서 사용하는 인증서 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.37.38.png&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9daxj/dJMcaaw6lAN/I3bBn1F2iVO07bEVsDFIT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9daxj/dJMcaaw6lAN/I3bBn1F2iVO07bEVsDFIT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9daxj/dJMcaaw6lAN/I3bBn1F2iVO07bEVsDFIT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9daxj%2FdJMcaaw6lAN%2FI3bBn1F2iVO07bEVsDFIT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;925&quot; height=&quot;170&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.37.38.png&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 등록한 인증서를 퍼블릭 인증서 요청으로 등록&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.37.44.png&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IFIy1/dJMcafFbdWP/5zLv8v9CiUAZjA4KrtSsA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IFIy1/dJMcafFbdWP/5zLv8v9CiUAZjA4KrtSsA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IFIy1/dJMcafFbdWP/5zLv8v9CiUAZjA4KrtSsA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIFIy1%2FdJMcafFbdWP%2F5zLv8v9CiUAZjA4KrtSsA1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;937&quot; height=&quot;373&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.37.44.png&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 퍼블릭 인증서 요청에서 원하는 도메인 등록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 도메인 등록시 특정한 하나의 도메인만 사용한다면 aaa.bbb.com으로 해도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 만약, 모든 서브도메인을 사용하고 싶다면 *.bbb.com으로 등록해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 내보내기는 인증서가 외부에서 필요한 경우인데 저는 ALB와 Route53에서 사용할 예정이라서 비활성화를 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 검증 방법은 Route53에서 사용할 예정이며 CNAME Route53에 자동으로 생성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.38.00.png&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xc5cX/dJMcadUQfXC/cOKRliJcp2FZINwuZbe9g1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xc5cX/dJMcadUQfXC/cOKRliJcp2FZINwuZbe9g1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xc5cX/dJMcadUQfXC/cOKRliJcp2FZINwuZbe9g1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxc5cX%2FdJMcadUQfXC%2FcOKRliJcp2FZINwuZbe9g1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;933&quot; height=&quot;593&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.38.00.png&quot; data-origin-width=&quot;933&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 인증서를 등록하면 상태를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저는 다른 서브도메인을 사용하기 위해서 *를 사용하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.38.32.png&quot; data-origin-width=&quot;1639&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v5O6z/dJMcahwdNiz/CxBbm585c2ckGuhKlOF1J1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v5O6z/dJMcahwdNiz/CxBbm585c2ckGuhKlOF1J1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v5O6z/dJMcahwdNiz/CxBbm585c2ckGuhKlOF1J1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv5O6z%2FdJMcahwdNiz%2FCxBbm585c2ckGuhKlOF1J1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1639&quot; height=&quot;253&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.38.32.png&quot; data-origin-width=&quot;1639&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 발급받은 인증서를 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CNAME 이름을 복사 후 Route53에서 검색을 해보면 CNAME으로 레코드가 생성된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.39.23.png&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/exap7z/dJMcag49QKW/D2ufA7qFQEdnRUSQaPIGr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/exap7z/dJMcag49QKW/D2ufA7qFQEdnRUSQaPIGr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/exap7z/dJMcag49QKW/D2ufA7qFQEdnRUSQaPIGr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fexap7z%2FdJMcag49QKW%2FD2ufA7qFQEdnRUSQaPIGr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1506&quot; height=&quot;794&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.39.23.png&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. Route53에서 CNAME을 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CNAME이 등록되는데 시간이 조금(몇 분) 소요됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.41.22.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEMrSB/dJMcachnpYk/990kAgo3ctSmfyrRAAsW11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEMrSB/dJMcachnpYk/990kAgo3ctSmfyrRAAsW11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEMrSB/dJMcachnpYk/990kAgo3ctSmfyrRAAsW11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEMrSB%2FdJMcachnpYk%2F990kAgo3ctSmfyrRAAsW11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;335&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.41.22.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7. Route53에서 AName으로 레코드를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존에 사용하던 ALB(로드밸런서)에 규칙으로 지정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.40.53.png&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0KLgZ/dJMcajtZgcI/N8FKM7vjcoyPZgZ6dibMlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0KLgZ/dJMcajtZgcI/N8FKM7vjcoyPZgZ6dibMlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0KLgZ/dJMcajtZgcI/N8FKM7vjcoyPZgZ6dibMlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0KLgZ%2FdJMcajtZgcI%2FN8FKM7vjcoyPZgZ6dibMlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1674&quot; height=&quot;646&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.40.53.png&quot; data-origin-width=&quot;1674&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8. ALB에 인증서를 등록해 주면 됩니다. 이후 ACM을 확인하면 연결된 리소스에 ALB를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.39.32.png&quot; data-origin-width=&quot;1495&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhnPp0/dJMcahXjbUt/c69XGhkxnqOpv2NMBQqCMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhnPp0/dJMcahXjbUt/c69XGhkxnqOpv2NMBQqCMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhnPp0/dJMcahXjbUt/c69XGhkxnqOpv2NMBQqCMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhnPp0%2FdJMcahXjbUt%2Fc69XGhkxnqOpv2NMBQqCMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1495&quot; height=&quot;189&quot; data-filename=&quot;스크린샷 2025-11-19 오후 5.39.32.png&quot; data-origin-width=&quot;1495&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sw-ym.tistory.com/200&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://sw-ym.tistory.com/200&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766154104622&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[AWS] 가비아 도메인 + ACM + ALB 연결 방법&quot; data-og-description=&quot;이번 시간에는 이전에 구입한 가비아 도메인을 기반으로 ACM 인증서를 생성하고, 이를 ALB에 연결하는 과정을 진행해본다. ACM(AWS Certificate Manager)은 AWS에서 제공하는 인증서 관리 서비스로, HTTPS 통&quot; data-og-host=&quot;sw-ym.tistory.com&quot; data-og-source-url=&quot;https://sw-ym.tistory.com/200&quot; data-og-url=&quot;https://sw-ym.tistory.com/200&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/0DjjH/hyZPW3AdOL/LVVzDYWKuVKkUId5Jmvmk1/img.png?width=596&amp;amp;height=596&amp;amp;face=0_0_596_596,https://scrap.kakaocdn.net/dn/VrKPh/hyZOJEXaPq/vsB2jaFfKWEvzokGtg19m0/img.png?width=596&amp;amp;height=596&amp;amp;face=0_0_596_596,https://scrap.kakaocdn.net/dn/bmeeh6/hyZPEotwv4/V8ynYIZt0tPXhHl92p3d30/img.png?width=2540&amp;amp;height=2212&amp;amp;face=0_0_2540_2212&quot;&gt;&lt;a href=&quot;https://sw-ym.tistory.com/200&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sw-ym.tistory.com/200&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/0DjjH/hyZPW3AdOL/LVVzDYWKuVKkUId5Jmvmk1/img.png?width=596&amp;amp;height=596&amp;amp;face=0_0_596_596,https://scrap.kakaocdn.net/dn/VrKPh/hyZOJEXaPq/vsB2jaFfKWEvzokGtg19m0/img.png?width=596&amp;amp;height=596&amp;amp;face=0_0_596_596,https://scrap.kakaocdn.net/dn/bmeeh6/hyZPEotwv4/V8ynYIZt0tPXhHl92p3d30/img.png?width=2540&amp;amp;height=2212&amp;amp;face=0_0_2540_2212');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[AWS] 가비아 도메인 + ACM + ALB 연결 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이번 시간에는 이전에 구입한 가비아 도메인을 기반으로 ACM 인증서를 생성하고, 이를 ALB에 연결하는 과정을 진행해본다. ACM(AWS Certificate Manager)은 AWS에서 제공하는 인증서 관리 서비스로, HTTPS 통&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sw-ym.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/AWS</category>
      <category>ACM</category>
      <category>AWS</category>
      <category>Route53</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/434</guid>
      <comments>https://baam.tistory.com/434#entry434comment</comments>
      <pubDate>Fri, 19 Dec 2025 23:16:43 +0900</pubDate>
    </item>
    <item>
      <title>SAM 을 이용한 AWS Lambda 배포</title>
      <link>https://baam.tistory.com/433</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Lambda에 배포하는 방법 중에 하나로 SAM을 이용하여 배포하는 방법이 있습니다. 오늘은 SAM으로 람다에 함수를 배포하고 관리할 수 있도록 구성했던 부분을 작성하도록 하겠습니다. (제가 진행했던 방식은 Mac에서 진행하였습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;혹시라도 부정확한 정보를 전달드릴 수 있습니다. 다만 틀린 부분이 있으면 댓글을 남겨주세요.&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. SAM 설치(&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/serverless-application-model/latest/developerguide/install-sam-cli.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;AWS 링크&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 환경에 맞게 SAM을 설치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764509615997&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Mac
brew tap aws/tap
brew install aws-sam-cli
sam --version # sam이 정상적으로 설치가 됐다면 버전이 나옵니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;201&quot; data-origin-height=&quot;33&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKg722/dJMcaiofhzC/tA2IgxhnJI97npE3ncPLQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKg722/dJMcaiofhzC/tA2IgxhnJI97npE3ncPLQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKg722/dJMcaiofhzC/tA2IgxhnJI97npE3ncPLQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKg722%2FdJMcaiofhzC%2FtA2IgxhnJI97npE3ncPLQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;201&quot; height=&quot;33&quot; data-origin-width=&quot;201&quot; data-origin-height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 설치 이후 명령어를 통해 SAM 관련 파일을 생성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764509404014&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sam init
# 이후 나타는 설정은 상황에 맞게 진행해주시면 됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;65&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ilwp1/dJMcabWXAnG/xwqG7n0kkEcTHWPVujiUN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ilwp1/dJMcabWXAnG/xwqG7n0kkEcTHWPVujiUN0/img.png&quot; data-alt=&quot;템플릿 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ilwp1/dJMcabWXAnG/xwqG7n0kkEcTHWPVujiUN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIlwp1%2FdJMcabWXAnG%2FxwqG7n0kkEcTHWPVujiUN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;331&quot; height=&quot;65&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;65&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;템플릿 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 템플릿을 지정하는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Quick Start Templates로 만들고 생성된 파일을 수정하셔도 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dvKm2l/dJMcagxcpW3/Kftj0gKk0OXmKXeEXEPQS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dvKm2l/dJMcagxcpW3/Kftj0gKk0OXmKXeEXEPQS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dvKm2l/dJMcagxcpW3/Kftj0gKk0OXmKXeEXEPQS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdvKm2l%2FdJMcagxcpW3%2FKftj0gKk0OXmKXeEXEPQS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;264&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Quick Start Templates 선택 시 어떤 템플릿을 배포할 건지 지정하는 부분입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zuam5/dJMcacVRZrc/IRIuFeMsS6ZnokWZ0kHWW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zuam5/dJMcacVRZrc/IRIuFeMsS6ZnokWZ0kHWW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zuam5/dJMcacVRZrc/IRIuFeMsS6ZnokWZ0kHWW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZuam5%2FdJMcacVRZrc%2FIRIuFeMsS6ZnokWZ0kHWW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;421&quot; data-origin-width=&quot;565&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;-&lt;span&gt; y/N 에서는 대문자로 된 부분이 엔터를 입력하면 자동으로 N으로 처리가 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;- 어떤 언어로 함수를 작성할지 지정합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-30 오후 10.37.28.png&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfxue2/dJMcagjFmtW/7IPTaDgHsy2JmNBqnqW7H0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfxue2/dJMcagjFmtW/7IPTaDgHsy2JmNBqnqW7H0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfxue2/dJMcagjFmtW/7IPTaDgHsy2JmNBqnqW7H0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfxue2%2FdJMcagjFmtW%2F7IPTaDgHsy2JmNBqnqW7H0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;359&quot; height=&quot;72&quot; data-filename=&quot;스크린샷 2025-11-30 오후 10.37.28.png&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 람다에 배포하기 위해서 Zip파일을 선택했습니다.(Image는 도커 이미지를 의미합니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daQklS/dJMcagjFmtC/MxepZnypx8wl5bbTN0PTbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daQklS/dJMcagjFmtC/MxepZnypx8wl5bbTN0PTbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daQklS/dJMcagjFmtC/MxepZnypx8wl5bbTN0PTbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaQklS%2FdJMcagjFmtC%2FMxepZnypx8wl5bbTN0PTbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;813&quot; height=&quot;214&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Nodejs 선택 시 typescript로 배포를 할지를 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 저는 나머지 부분에서 default로 선택하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAUarY/dJMcaaX3zOw/4G9Mh2dNegwfpAxKpvSLp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAUarY/dJMcaaX3zOw/4G9Mh2dNegwfpAxKpvSLp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAUarY/dJMcaaX3zOw/4G9Mh2dNegwfpAxKpvSLp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAUarY%2FdJMcaaX3zOw%2F4G9Mh2dNegwfpAxKpvSLp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;262&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이후 제가 설정한 대로 sam 폴더가 생성되면 내부에는 원하는 설정들이 된 파일들이 생성돼 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. samconfig, template 파일 확인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- samconfig.toml 파일은 배포를 진행할 때 AWS에서 사용할 부분들을 지정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764511329182&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 하단 URL에 접속 시 어떤 키와 값으로 작성해야 하는지 정보를 알 수 있습니다.
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
version = 0.1

# global.parameters는 전역에서 사용할 수 있는 영역이라고 보면 됩니다.
# 각 영역에서 stack_name을 따로 지정하지 않았다면 global.parameters를 사용합니다.
[default.global.parameters]
stack_name = &quot;sam-app&quot;

# build 할때 parameter 입니다.
[default.build.parameters]
cached = true # 빌드 시 변경 사항이 없다면 캐시된 결과를 재사용 합니다.
parallel = true # 함수가 여러개 있을 때 병렬로 배포합니다.

# 유효성 검증
[default.validate.parameters]
lint = true

# 배포시 사용하는 파라미터 입니다.
[default.deploy.parameters]
capabilities = &quot;CAPABILITY_IAM&quot; # CloudFormation이 리소스를 생성하거나 수정할 수 있도록 허용합니다.
confirm_changeset = true # CloudFormation 변경을 사용자에게 보여줍니다.
resolve_s3 = true # S3 버킷을 자동으로 생성하거나 기존의 버킷을 재사용하도록 설정합니다.

[default.package.parameters]
resolve_s3 = true # s3 버킷을 지정하지 않았다면 자동으로 버킷을 찾거나 생성합니다.(지정했다면 false로 유지)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- template.yaml 파일은 Lambda 구성을 설정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1764511457999&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Default로 생성된 파일입니다.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: &amp;gt;
  sam-app

  Sample SAM Template for sam-app
  
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs22.x
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: &quot;API Gateway endpoint URL for Prod stage for Hello World function&quot;
    Value: !Sub &quot;https://${ServerlessRestApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/Prod/hello/&quot;
  HelloWorldFunction:
    Description: &quot;Hello World Lambda Function ARN&quot;
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: &quot;Implicit IAM Role created for Hello World function&quot;
    Value: !GetAtt HelloWorldFunctionRole.Arn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Resources 영역에 AWS에 있는 다른 서비스들은 연결하거나 Event Handler, VPC Config 등을 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 배포 진행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1764511654366&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 처음 build 시에는 --guided를 필수로 입력하시고 이후에는 build만 진행하셔도 됩니다.
sam build --guided
sam deploy&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 정상적으로 deploy가 된 다면 각각의 AWS에서 확인할 실수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-14 오후 12.29.07.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1ncZR/dJMcaiht6FR/DprCPkd1RiyciKjGckMZB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1ncZR/dJMcaiht6FR/DprCPkd1RiyciKjGckMZB1/img.png&quot; data-alt=&quot;CloudFormation&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1ncZR/dJMcaiht6FR/DprCPkd1RiyciKjGckMZB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1ncZR%2FdJMcaiht6FR%2FDprCPkd1RiyciKjGckMZB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;421&quot; data-filename=&quot;스크린샷 2025-11-14 오후 12.29.07.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;CloudFormation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-14 오후 12.30.29.png&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdIhgC/dJMcadAqqdx/VFhp5iFhoPeFKUWmnAUtBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdIhgC/dJMcadAqqdx/VFhp5iFhoPeFKUWmnAUtBK/img.png&quot; data-alt=&quot;S3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdIhgC/dJMcadAqqdx/VFhp5iFhoPeFKUWmnAUtBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdIhgC%2FdJMcadAqqdx%2FVFhp5iFhoPeFKUWmnAUtBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1565&quot; height=&quot;372&quot; data-filename=&quot;스크린샷 2025-11-14 오후 12.30.29.png&quot; data-origin-width=&quot;1565&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;S3&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;lambda-application.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ5AG7/dJMcadmS8kp/CeGUeTAp07srbNO9ezacrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ5AG7/dJMcadmS8kp/CeGUeTAp07srbNO9ezacrk/img.png&quot; data-alt=&quot;lambda-applicaion&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ5AG7/dJMcadmS8kp/CeGUeTAp07srbNO9ezacrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ5AG7%2FdJMcadmS8kp%2FCeGUeTAp07srbNO9ezacrk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1656&quot; height=&quot;186&quot; data-filename=&quot;lambda-application.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;lambda-applicaion&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-14 오후 5.31.38.png&quot; data-origin-width=&quot;1779&quot; data-origin-height=&quot;146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwvk8j/dJMcai2OeH2/QHUWxJSKDJaZbNZA5hoOH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwvk8j/dJMcai2OeH2/QHUWxJSKDJaZbNZA5hoOH0/img.png&quot; data-alt=&quot;lambda-function&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwvk8j/dJMcai2OeH2/QHUWxJSKDJaZbNZA5hoOH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwvk8j%2FdJMcai2OeH2%2FQHUWxJSKDJaZbNZA5hoOH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1779&quot; height=&quot;146&quot; data-filename=&quot;스크린샷 2025-11-14 오후 5.31.38.png&quot; data-origin-width=&quot;1779&quot; data-origin-height=&quot;146&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;lambda-function&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-14 오후 5.31.55.png&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bceJmE/dJMcabJreFC/t4nBjxMpRYYZfiS4jvTdUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bceJmE/dJMcabJreFC/t4nBjxMpRYYZfiS4jvTdUK/img.png&quot; data-alt=&quot;lambda-function-detail&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bceJmE/dJMcabJreFC/t4nBjxMpRYYZfiS4jvTdUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbceJmE%2FdJMcabJreFC%2Ft4nBjxMpRYYZfiS4jvTdUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1485&quot; height=&quot;501&quot; data-filename=&quot;스크린샷 2025-11-14 오후 5.31.55.png&quot; data-origin-width=&quot;1485&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;lambda-function-detail&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deploy 진행하다가 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;실패&lt;/span&gt;&lt;/b&gt;한다면???&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실패시 Cloudformation 스택에서 failed된 스택을 삭제하고 문제가 있던 samconfig 파일을 수정하고 deploy를 진행해야 합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재는 로컬에서 명령어로 배포하는 부분을 진행하였지만, 이후에는 github-action을 이용해서 배포 자동화를 하는 부분도 작성할 수 있도록 하겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 lambda가 불편하고 어렵다고 생각했지만 가벼운 기능들을 자주 사용하다보면 더 확장된 기능들을 사용해 볼 수 있을 것이라고 생각하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ref.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@leejm_dev/AWS-SAM&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@leejm_dev/AWS-SAM&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1764509001828&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;AWS SAM&quot; data-og-description=&quot;how to use AWS SAM with cloudformation&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@leejm_dev/AWS-SAM&quot; data-og-url=&quot;https://velog.io/@leejm_dev/AWS-SAM&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5xGEc/hyZOCxmzJ7/ukBhtceN5nkvq53cupnlKk/img.png?width=2730&amp;amp;height=1232&amp;amp;face=0_0_2730_1232,https://scrap.kakaocdn.net/dn/bnrX3D/hyZOjF56r2/dNR5LsgMIxsLXF6mwvS2L0/img.png?width=2730&amp;amp;height=1232&amp;amp;face=0_0_2730_1232,https://scrap.kakaocdn.net/dn/Hpl34/hyZNGH9NmR/Q8MzHcF62nvGv7KCLbFTIK/img.jpg?width=3024&amp;amp;height=4032&amp;amp;face=0_0_3024_4032&quot;&gt;&lt;a href=&quot;https://velog.io/@leejm_dev/AWS-SAM&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@leejm_dev/AWS-SAM&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5xGEc/hyZOCxmzJ7/ukBhtceN5nkvq53cupnlKk/img.png?width=2730&amp;amp;height=1232&amp;amp;face=0_0_2730_1232,https://scrap.kakaocdn.net/dn/bnrX3D/hyZOjF56r2/dNR5LsgMIxsLXF6mwvS2L0/img.png?width=2730&amp;amp;height=1232&amp;amp;face=0_0_2730_1232,https://scrap.kakaocdn.net/dn/Hpl34/hyZNGH9NmR/Q8MzHcF62nvGv7KCLbFTIK/img.jpg?width=3024&amp;amp;height=4032&amp;amp;face=0_0_3024_4032');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;AWS SAM&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;how to use AWS SAM with cloudformation&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공부/AWS</category>
      <category>AWS</category>
      <category>lambda</category>
      <category>SAM</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/433</guid>
      <comments>https://baam.tistory.com/433#entry433comment</comments>
      <pubDate>Sun, 30 Nov 2025 23:33:11 +0900</pubDate>
    </item>
    <item>
      <title>모바일 카드 만들기</title>
      <link>https://baam.tistory.com/432</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요즘은 여러 행사들이 많이 있습니다. 결혼식, 돌잔치, 칠순, 행사 등 다양한 이벤트들이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 이벤트에는 링크를 보내서 친구, 가족 등 사람들에게 링크를 전달하고 소식을 전하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돈을 주고 요청을 하면 좋은 서비스를 이용할 수 있는지 저는 잘 모르지만 모두가 쉽게 만들고 사용할 수 있는 서비스를 만들어서 공유하고 싶다는 생각이 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 만들면 의미가 더 있을 수 도 있고 돈도 절약할 수 있다는 생각에서 시작하였습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 스프레드 시트에 아래 값을 넣어주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQScXD/dJMcabWP8Jb/0r5R5S1yCYKdUegR8Azh60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQScXD/dJMcabWP8Jb/0r5R5S1yCYKdUegR8Azh60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQScXD/dJMcabWP8Jb/0r5R5S1yCYKdUegR8Azh60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQScXD%2FdJMcabWP8Jb%2F0r5R5S1yCYKdUegR8Azh60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;453&quot; height=&quot;496&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- A 열에 동일한 값을 넣어주시고 B열에는 보여주고 싶은 값을 넣으시면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1762870581790&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;groom_name - 신랑 이름
bride_name - 신부 이름
wedding_date - 결혼식 날짜
wedding_location - 결혼식 장소
invitation_title - 제목
invitation_text - 내용
groom_father_name - 신랑 아버지 이름
groom_mother_name - 신랑 어머니 이름
bride_father_name - 신부 아버지 이름
bride_mother_name - 신부 아버지 이름
groom_phone - 신랑 전화번호
bride_phone - 신부 전화번호
groom_father_phone - 신랑 아버지 전화번호
groom_mother_phone - 신부 어머니 전화번호
bride_father_phone - 신부 아버지 전화번호
bride_mother_phone - 신부 어머니 전화번호
main_background_img - 메인 이미지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 이미지는 구글 드라이브에 넣어주세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-09 오후 11.17.45.png&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;48&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4EvBd/dJMcafx9YJn/og7v1O8fptrU14mxdUYZgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4EvBd/dJMcafx9YJn/og7v1O8fptrU14mxdUYZgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4EvBd/dJMcafx9YJn/og7v1O8fptrU14mxdUYZgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4EvBd%2FdJMcafx9YJn%2Fog7v1O8fptrU14mxdUYZgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;48&quot; data-filename=&quot;스크린샷 2025-11-09 오후 11.17.45.png&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;48&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이미지를 업로드한 후 우측 점 3개를 누르세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QRrRs/dJMcagKBnTd/0KGYRVrld4Si4ISVB3Hkqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QRrRs/dJMcagKBnTd/0KGYRVrld4Si4ISVB3Hkqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QRrRs/dJMcagKBnTd/0KGYRVrld4Si4ISVB3Hkqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQRrRs%2FdJMcagKBnTd%2F0KGYRVrld4Si4ISVB3Hkqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;132&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공유를 클릭하세요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDNdeQ/dJMcagjw6yw/nKfZ7gsvadFpjxJ2A370Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDNdeQ/dJMcagjw6yw/nKfZ7gsvadFpjxJ2A370Zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDNdeQ/dJMcagjw6yw/nKfZ7gsvadFpjxJ2A370Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDNdeQ%2FdJMcagjw6yw%2FnKfZ7gsvadFpjxJ2A370Zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;190&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cW8phU/dJMcac9g1H8/PCqu8Owqac1rvN2nNyBUwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cW8phU/dJMcac9g1H8/PCqu8Owqac1rvN2nNyBUwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cW8phU/dJMcac9g1H8/PCqu8Owqac1rvN2nNyBUwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcW8phU%2FdJMcac9g1H8%2FPCqu8Owqac1rvN2nNyBUwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;92&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;92&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 제한됨으로 된 상태를 링크가 있는 모든 사용자로 변경하면서 모든 사용자는 뷰어로 지정해 주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이후 링크복사를 클릭하여 이미지 링크를 복사합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOUDpb/dJMcabbsYX7/55YBhIGkEIpP0ClMGZtxpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOUDpb/dJMcabbsYX7/55YBhIGkEIpP0ClMGZtxpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOUDpb/dJMcabbsYX7/55YBhIGkEIpP0ClMGZtxpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOUDpb%2FdJMcabbsYX7%2F55YBhIGkEIpP0ClMGZtxpk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;244&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 복사한 링크를 스프레드시트에 갤러리 탭에 넣어줍니다.(1번 줄은 비워두시면 됩니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 안내사항&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 안내사항이라는 탭을 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 1번 행은 비워둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- A열은 주소, 지하철, 주차장을 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- B열은 해당 A열에 해당하는 값을 작성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X4jOF/dJMcah3PbSa/ocldNbx4ZMVXh2FsvFkR8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X4jOF/dJMcah3PbSa/ocldNbx4ZMVXh2FsvFkR8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X4jOF/dJMcah3PbSa/ocldNbx4ZMVXh2FsvFkR8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX4jOF%2FdJMcah3PbSa%2FocldNbx4ZMVXh2FsvFkR8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;496&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 코드 구성 및 배포&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TJeRP/dJMcaaRafJN/MB74NigG5NTro0krWlCQUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TJeRP/dJMcaaRafJN/MB74NigG5NTro0krWlCQUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TJeRP/dJMcaaRafJN/MB74NigG5NTro0krWlCQUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTJeRP%2FdJMcaaRafJN%2FMB74NigG5NTro0krWlCQUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;175&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Apps Script를 선택합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RikOz/dJMcaacyFJP/5vX9BKUK7vcPrhAbnirQe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RikOz/dJMcaacyFJP/5vX9BKUK7vcPrhAbnirQe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RikOz/dJMcaacyFJP/5vX9BKUK7vcPrhAbnirQe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRikOz%2FdJMcaacyFJP%2F5vX9BKUK7vcPrhAbnirQe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;223&quot; height=&quot;182&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 4개의 파일 생성합니다.( 파일 생성은 + 버튼을 누르면 가능합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 4개의 파일은 압축 파일을 다운로드하신 다음에 복사해서 넣으시면 되겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;fileblock&quot; data-ke-align=&quot;alignCenter&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/2Kpnj/dJMcafyaCEr/GZoVUpZrKLlcqVUOzoMtG1/%E1%84%8B%E1%85%A1%E1%86%B8%E1%84%8E%E1%85%AE%E1%86%A8%E1%84%91%E1%85%A1%E1%84%8B%E1%85%B5%E1%86%AF.zip?attach=1&amp;amp;knm=tfile.zip&quot; class=&quot;&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;/div&gt;
    &lt;div class=&quot;desc&quot;&gt;&lt;div class=&quot;filename&quot;&gt;&lt;span class=&quot;name&quot;&gt;압축파일.zip&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;size&quot;&gt;0.01MB&lt;/div&gt;
&lt;/div&gt;
  &lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Code.gs 파일에는 스프레드 시트 주소를 넣어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsyKbk/dJMcaaXVON2/t1OkkeaI6VtYWW9AFSutkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsyKbk/dJMcaaXVON2/t1OkkeaI6VtYWW9AFSutkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsyKbk/dJMcaaXVON2/t1OkkeaI6VtYWW9AFSutkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsyKbk%2FdJMcaaXVON2%2Ft1OkkeaI6VtYWW9AFSutkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;737&quot; height=&quot;34&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스프레드 시트 주소는 흰색으로 가린 부분이라고 생각하시면 됩니다. 이 주소는 스프레드시트 상단 주소에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 흰색으로 가린 부분의 값을 복사하여 Code.gs에 넣어줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-11-12 오전 12.44.12.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUM18Y/dJMcafZeZgq/IpFtXRn6s2kgbFJFXNVVZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUM18Y/dJMcafZeZgq/IpFtXRn6s2kgbFJFXNVVZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUM18Y/dJMcafZeZgq/IpFtXRn6s2kgbFJFXNVVZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUM18Y%2FdJMcafZeZgq%2FIpFtXRn6s2kgbFJFXNVVZ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;744&quot; height=&quot;185&quot; data-filename=&quot;스크린샷 2025-11-12 오전 12.44.12.png&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 모든 파일을 생성하고 코드를 넣었다면 배포 버튼을 클릭하여 새배포를 클릭합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhxszi/dJMcacOYTK2/DsJuwYYqsd96mdnbJKZe8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhxszi/dJMcacOYTK2/DsJuwYYqsd96mdnbJKZe8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhxszi/dJMcacOYTK2/DsJuwYYqsd96mdnbJKZe8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbhxszi%2FdJMcacOYTK2%2FDsJuwYYqsd96mdnbJKZe8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;397&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 설명에는 원하시는 글자를 넣어준 후 배포버튼을 클릭합니다.(넣을 값이 없다면 배포 1이라고 넣으셔도 됩니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9wWZL/dJMcai2Js9F/9TOgQ8h5SB0VCAkKSkOsR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9wWZL/dJMcai2Js9F/9TOgQ8h5SB0VCAkKSkOsR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9wWZL/dJMcai2Js9F/9TOgQ8h5SB0VCAkKSkOsR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9wWZL%2FdJMcai2Js9F%2F9TOgQ8h5SB0VCAkKSkOsR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;369&quot; height=&quot;291&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 웹앱에 URL 부분에 파란색 주소가 이제 모바일 카드 주소입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주소를 복사해서 사용하시면 모바일 카드를 확인하실 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/OIcYUvnlyWo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;시연 영상&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=OIcYUvnlyWo&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/JXIQH/dJMb86nVnt7/1RLDyP2ljBlRAzzke67Gdk/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360,https://scrap.kakaocdn.net/dn/bdtXCO/dJMb8QL9O06/luRmXRPxeiTjOyGeHPsR2k/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360,https://scrap.kakaocdn.net/dn/bvKEyC/dJMb8UHM0Oq/JB4eUqqiDCAZZVmy5k5An1/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360&quot; data-video-width=&quot;480&quot; data-video-height=&quot;360&quot; data-video-origin-width=&quot;480&quot; data-video-origin-height=&quot;360&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;spread sheets를 활용한 mobile card 만들기&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/OIcYUvnlyWo&quot; width=&quot;480&quot; height=&quot;360&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;시연 영상&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명이 어려우실 수도 있지만 혹시라도 사용하실 때 모르시는 부분이 있으시다면 댓글 남겨주시면 도움을 드릴 수 있도록 하겠습니다.&lt;br /&gt;조금 씩 사용성이나 부족한 부분을 고쳐나갈 수 있도록 해보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불편한점이 있으시면 피드백을 주시는건 언제나 환영입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-25년 10월 -&amp;nbsp;&lt;/p&gt;</description>
      <category>월간 프로젝트</category>
      <category>모바일카드</category>
      <category>월간프로젝트</category>
      <author>조용한외침</author>
      <guid isPermaLink="true">https://baam.tistory.com/432</guid>
      <comments>https://baam.tistory.com/432#entry432comment</comments>
      <pubDate>Wed, 12 Nov 2025 00:55:30 +0900</pubDate>
    </item>
  </channel>
</rss>