All
14 posts
무중단 배포 과정에서의 불확실성 개선 [2] - AWS CLI의 wait API 적용

배경 이번 글에서 다루는 deregister-targets 등의 개념은 1편에 작성되어 있습니다..ㅎㅎ 1편을 먼저 읽고 이번 글을 읽어주시면 감사하겠습니다. 지난 글에서 통계량을 이용해 deregister-targets 호출 이후 실제 트래픽 중단까지 소요되는 최대 시간을 15초로 잡았었습니다. 지난번에 계산했던 트래픽 중단까지의 시간이 15초를 초과할 확률은 0.000021% 로 극히 미미한 수준이지만, 그럼에도 발생 가능성이 있다는 찝찝함은 있었던 것 같습니다. 그래서.. aws-cli의 elbv2 API 문서를 하나씩 살펴보던 도중 wait이라는 키워드를 발견했는데요, 이번 글에서는 wait을 이용하여 기존의 불확실성을 더 개선하는 방법을 소개하겠습니다. wait API Wait until a particular condition is satisfied. Each subcommand polls an API until the listed requirement is met. 공…

무중단 배포 과정에서의 불확실성 개선 [1] - AWS CLI를 이용한 등록 취소

배경 기존 방법은, 의도적으로 헬스체크에 실패하도록 하는 API를 호출하여 ELB(로드 밸런서)의 트래픽 배분을 막는 방법이었습니다. 팀원인 테니가 상황에 맞게 훌륭하게 구현해줬고, 문제될 여지가 거의 없다고는 느껴지지만 아래의 불확실성은 있다는 생각이 들었습니다. 해당 API의 호출 시점, 그리고 서버의 상황에 따라 실제 트래픽 중단까지 최대 10초에 가까운 시간 차이가 발생합니다. 배포 작업이 애플리케이션의 영향을 받습니다. 현재는 API를 호출하여 헬스체크에 사용되는 불리언 필드를 false로 바꿔 실패하도록 하고 있는데, 이걸 다시 true로 바꾸는 기능은 없습니다. 즉 배포 과정에서의 사소한 실수로 불필요하게 해당 API가 호출된다면 Springboot를 다시 배포해야 합니다. 기존 방법에 대한 자세한 내용은 이전 글 을 참고해주세요! 두개의 원인을 보면 결국 API를 다른 무언가로 대체하는 것이 해결 방법인 것 같은데요, 물론 다른 해결 방법도 있겠지만 이번 글에서는 A…

무중단 배포 과정에서의 매모리 사용량 개선 - 기존 애플리케이션의 종료 시점 변경

배경 프로젝트 팀원인 테니가 기존의 무중단 배포에서 다운타임이 발생하는 문제를 해결하는 PR을 올렸습니다. 자세한 내용은 해당 PR에서 확인하실 수 있습니다. 문제도 잘 파악했고, 해결까지의 전체 과정을 문서화해서 공유해준 덕분에 전체 과정을 빠르게 이해할 수 있었는데요(테니 감사합니다 😄), 제가 PR에 남겼던 댓글 중 일부를 직접 테스트해본 결과를 바탕으로 기존 방법에서 조금 더 개선이 가능한 부분을 공유하고자 합니다. 기존 배포 과정 개선 방법을 작성하기 전에, 기존의 과정을 순서대로 살펴보며 전체 흐름을 다시 정리하겠습니다. 1. 인스턴스 내부에서 POST /termination 요청을 보내 ELB의 헬스체크에 실패하도록 지정 ELB의 헬스체크에 사용되는 코드를 살펴보겠습니다. 메서드는 필드에 있는 AtomicBoolean 값이 이면 ELB가 로 헬스체크 요청을 보낼 때 을 보내는 코드입니다. ELB에는 헬스체크 정상 응답 코드가 200으로 지정되어 있습니다. 메서드는…

AWS ELB 와 Springboot 에서의 클라이언트 IP 확인

배경 팀원의 코드를 리뷰하고 있었는데, 아래와 같은 코드가 있었습니다. 실제 코드와 다르며 내용 파악을 위해 일부 수정하였습니다. 내용은 중요하지 않긴 하지만.. 간단하게만 말씀드리면 ELB에서의 헬스체크를 의도적으로 실패하도록 하는 API 입니다. 이 코드에서의 핵심은 클라이언트의 IP(=remoteHost)가 로컬호스트인 경우에만 OK 응답을 주겠다는 건데, 여기서 의문이 들었던 것은 “Nginx 설정과 무관하게 getRemoteHost()가 항상 실제 클라이언트의 IP를 반환할 것을 보장하는지?” 였는데요, 의문이 들었던 내용부터 실제 테스트를 하며 확인한 결과를 하나하나 작성해 보겠습니다. 시작하기 전에, 이번 글에서 사용하는 인프라 구조입니다! 질문 이전 문단에 나왔던 코드의 getRemoteHost() 부분에 아래와 같은 질문을 남겼었습니다. 찝찝했던 이유는 이전에 ELB를 사용하기 전 EC2로 직접 요청을 보냈을 때 클라이언트 정보를 읽어오지 못해 문제가 발생했던 …

API Break Change 방지를 위한 리스트 형태의 JSON 응답 수정

배경 우아한테크코스의 세 번째 미션을 진행하며, 리뷰어께서 좋은 의견을 주셨습니다. 웹 구현을 처음 해보는 입장에서 생각지도 못했던 에 대한 것인데요, API JSON 응답은 List가 아닌 Object 형식으로 하는 것을 권하셨고 이번 글에서는 이 내용에 대해 기록해보고자 합니다. API Break Change란? 출처에 있는 글에서는 Breaking Change를 다음과 같이 설명합니다. Abreaking changeis when one such change causes a client application to break somehow. While some changes make a minimal impact, breaking changes are those fundamental changes that cause the system to cease functioning. This could be a change in a field name, the removal of an …

Spring Data JPA에서의 동적 쿼리 2 - Specification 활용

목표 이번 글에서는, 지난 편에서 알아본 Specification을 이용해 로 예약을 조회하는 동적 쿼리를 만들어 보겠습니다.😄 기본 코드 Entity 이전 1편에 작성된 내용이지만, 다시 한번 작성하겠습니다. Member, Theme, ReservationTIme는 다음과 같이 구성되어 있습니다. Member: 및 에 해당되는 을 필드로 가집니다. Theme: ID 및 에 해당되는 을 필드로 가집니다. ReservationTime: ID 및 에 해당되는 을 필드로 가집니다. API 를 통해 호출하며, 쿼리 파라미터를 이용합니다. 파라미터의 구성은 다음과 같습니다. 값을 선택하지 않는 경우는 전체를 조회합니다. 회원(memberId): 선택한 회원의 ID(PK)값. 테마(themeId): 회원과 동일합니다. 시작 날짜(startDate): 선택한 날짜도 범위에 포함하며, 형식입니다. 종료 날짜(endDate): 시작 날짜와 동일합니다. Endpoint 설정은 Java…

Spring Data JPA 에서의 동적 쿼리 1 - Specification 탐구

배경 우아한테크코스의 세 번째 스프링 미션의 첫 번째 요구사항은, 기존의 JDBC 구현을 Spring Data JPA로 바꾸는 것이었습니다. 다른 부분은 크게 어렵지 않았지만, 관리자가 특정 조건에 해당하는 예약을 조회하는 기능이 그나마(?) 어려웠던 것 같습니다. 예약 검색은 를 이용하는 것이었는데요, 저는 이전 미션에서 이 네 가지 값이 모두 입력되지 않으면 예외를 발생시켰는데 페어는 동적 쿼리를 이용해서 네가지 값이 모두 입력되지 않으면 전체 예약을 조회하고, 하나의 값만 입력되도 그 값으로 조회하도록 구현을 했었습니다. 결과적으로는 동적 쿼리로 조회하는 것이 사용자에게 더 편하겠다고 생각했고, Spring Data JPA의 Specification을 이용하여 이를 구현할 수 있었습니다. 그래서 이번 글에서는 Specification에 대해 다루고, 다음 글에서는 이를 활용하여 문제를 해결해 보겠습니다😄 Specification 알아보기 개요 JPA 2 introduces …

날짜와 시간의 직렬화 & 역직렬화 시도 - JsonFormat, ObjectMapper

배경 우아한테크코스의 세 번째 스프링 미션을 진행하며 코드 리뷰를 받던 도중 리뷰어께서 좋은 의견을 공유해 주셨습니다. 의견은 기존의 을 중복해서 사용하는 코드를 ObjectMapper를 만들어 해결하는 것이었는데요, 여기에 더해서, 미션을 하며 그냥 대략적으로만 알고 사용했던 @JsonFormat에 대해서도 알아보는 과정을 기록하고자 합니다. @JsonFormat 이란? 공식 문서에 따르면, JsonFormat을 다음과 같이 설명합니다. General-purpose annotation used for configuring details of how values of properties are to be serialized. Unlike most other Jackson annotations, annotation does not have specific universal interpretation: instead, effect depends on datatype of propert…

회원 역할에 따른 API 접근 - Spring Interceptor

배경 우아한테크코스의 두 번째 미션에서의 새로운 요구사항은 JWT를 이용해 로그인 기능을 구현하고, 역할에 따라 접근 권한을 다르게 설정하는 것이었습니다. 요구사항에 명시된 것은 로그인 된 회원이 직접 예약을 추가하는 기능 구현과 관리자 페이지 접근을 제한하는 것이었는데요, 이 두 가지와 더불어 관리자와 회원의 역할을 조금 더 명확하게 나누기 위해 Spring Interceptor를 이용했던 경험을 기록하고자 합니다. 글에서는 스프링 인터셉터를 두 가지 방식으로 구현합니다. 먼저 경로를 지정하는 방법을 사용하여 구현한 후, 커스텀 어노테이션을 이용한 방법으로 개선해보겠습니다. 또한, ArgumentResolver를 사용하는 관점에 대해서도 작성하겠습니다. API 분류 우선, 현재 있는 API를 역할별로 구분해볼 필요가 있을 것 같습니다. 1. 로그인을 하지 않아도 접근 가능 Endpoint HTTP Method 기능 /login POST 로그인 요청 /themes/weekly GE…

스프링 입력에서의 예외 처리 여정 3 - Custom Deserializer

서론 드디어 마지막이네요. 처음 이 글을 작성하고자 했을 때는 3편까지 쓸 것이라는 생각을 전혀 안했는데..ㅎㅎ 최대한 간단하게 쓴다고 노력해도 역시 욕심은 끝이 없는 것 같습니다. 이번에는 지난번에 학습한 JsonNode를 활용해서 Custom Deserializer를 직접 만들어 볼텐데요, JsonNode가 헷갈리신다면 이전 편를 확인해주시면 감사하겠습니다.🙇 해결해야 할 것 이번 Custom Deserializer를 통해 해결해야 하는 문제는, 1편의 맨 마지막에 나온 두 가지 문제입니다. 본문을 시작하기 전에 간단하게 리마인드 하고 가겠습니다 ㅎㅎ 지금의 예외 핸들링은, 테마, 시간, 멤버를 선택하지 않았을 때 기본 입력값인 등에 의존합니다. 즉 클라이언트 코드에 완전히 의존하는 구조입니다. 예약을 추가할 때 멤버, 시간, 테마 중 2개 이상의 값이 입력되지 않아도 하나의 값만 표시됩니다. 즉 입력되지 않은 모든 값을 예외 메시지에 담을 수 없습니다. 추가적으로, 날짜를 …

스프링 입력에서의 예외 처리 여정 2 - JsonNode

이전 편 내용 요약 어노테이션의 검증을 통해 발생하는 예외는 입니다. 테마, 시간 추가의 경우 값을 입력하지 않으면 빈 문자열()으로 JSON에 담겼기에 를 통해 이 발생했습니다. 예약을 추가할 때는 회원, 날짜, 테마, 시간을 입력받습니다. 날짜의 경우 입력하지 않으면 빈 문자열로 요청 JSON에 담기기에() MethodArgumentNotValidException이 발생합니다. 회원, 테마, 시간은 입력하지 않으면 각각 빈 문자열이 아닌 으로 값이 담기고, 이 값을 Long 타입으로 파싱할 수 없습니다. 따라서 역직렬화 과정에서 InvalidFormatException이 발생하고, Jackson은 이 예외를 HttpMessageNotReadableException으로 던집니다. 결론: 하나의 요청에서 다른 타입의 예외들이 발생하는데 이 예외를 한 번에 처리할 수가 없었습니다. Custom Deserializer 시작 지금 현재의 문제를 크게 보면 역직렬화 과정에서 발생…

스프링 입력에서의 예외 처리 여정 1 - 문제 인식 및 일부 해결

배경 우아한테크코스의 두 번째 스프링 미션을 통해 처음으로 스프링의 예외 처리를 경험하게 되었습니다. 미션의 요구 사항은 예약, 테마, 시간 생성시 발생하는 예외를 적절하게 처리하는 것이었는데요, 와 것과 같은 예외 처리는 크게 어렵지 않았으나 입력에서의 예외, 즉 입력되지 않은 값 등 값 자체가 잘못되었을 때에 대한 처리가 가장 어려웠습니다. 가장 많이 헤맸던 이유는 요청 상황마다 발생하는 예외 타입이 달랐기 때문인데요, 이번 글에서는 해당 문제와 이 문제를 해결해가는 과정에 대해 기록해보려 합니다. 주의 스프링을 이번에 처음 사용하게 되어, 내용이 매우 부실할 수 있습니다. 이 글은 지식을 전달하는 글이 아닌, 개인의 시행착오 과정을 기록하는 글임을 감안해주시면 감사하겠습니다.🙇 API 구성 API - 관리자가 직접 예약을 추가 관리자가 직접 예약을 추가할 땐, 예약 페이지에서 이미 등록된 회원, 테마, 시간과 날짜를 선택합니다. 이때 회원, 테마, 시간은 DB에 저장된 I…

Layered Architecture 적용 과정에서의 DTO 사용에 대한 고민

배경 우아한테크코스의 첫 스프링 미션을 진행하며, 처음으로 레이어드 아키텍쳐를 적용해보는 경험을 하게 되었습니다. Controller에서 JdbcTemplate을 사용하여 DB 처리를 하는 기존 코드에 Service - Repository 계층을 추가하는 과정에서 자연스레 들었던 고민은 도메인과 DTO의 처리를 어떻게 해야할지였고, 이때 가졌던 생각의 흐름을 정리해보고자 합니다. 아래의 예시는 예약을 추가(저장) 하는 기능을 바탕으로 작성하였습니다😄 개요 API의 구성 예약을 추가할 때는 을 JSON 형태로 요청합니다. 예약 시간은 별도의 데이터베이스 테이블에 저장을 해둔 뒤, 시간의 ID값을 이용하여 조회합니다. 요청이 들어오면 ID값은 자동으로 채운 뒤(AUTO_INCREMENT), 입력된 시간 ID에 해당되는 예약 시간까지 불러와서 저장된 예약 내역을 응답하는 구조입니다. 도메인 여기서 날짜와 시간은 각각 LocalDate와 LocalTime을 사용할 수 있으나, 이번 단계…

안녕하세요!

안녕하세요. 귀여운 상돌이 보고 즐거운 하루 보내세요!