Programming

    [SpringBoot] 스크롤 API (Offset vs Keyset-Filtering)

    실무에서는 대량의 데이터를 조회한 뒤 수정하는 일이 종종 발생합니다. 예를 들어, 브랜드명이 매일우유에서 매일유업으로 바뀐다면 판매상품 내 brandName 필드값이 매일우유인 엔티티를 모두 찾아 브랜드명을 매일유업으로 바꿔주어야 합니다. 이때 변경해야할 엔티티가 수만건이라면 페이징 처리를 안할 시 Out-of-Memory가 발생할 수 있습니다. 따라서, 우리가 흔히 부르는 스크롤 방식으로 100건씩 끊어서 조회한 뒤 update를 하는 방법을 취하게 됩니다. SpringBoot 3.1 버전부터는 스크롤 API를 공식적으로 지원합니다. Spring Data JPA 3.1 버전을 추가함으로써 우리는 스크롤 방식의 페이징 처리를 간단하게 구현할 수 있습니다. 이번 글에서는 Offset 방식과 Keyset-F..

    온디맨드 리사이징 구현기(feat. CloudFront, Lambda@Edge)

    상황 데이원 프로젝트는 매일 운동 인증샷을 남기고 캘린더로 확인할 수 있는 애플리케이션입니다. 이때 사용자가 원본이미지를 업로드하면, 캘린더에서 보여줄 썸네일 이미지(39px*39px)와 개별 이미지(390px*390px) 이미지가 필요했습니다. 처음에는 단순히 원본, 썸네일, 개별 이미지를 모두 S3 버킷에 저장하려고 했습니다. 그러나 만약 이미지 정책이 바뀌어 썸네일 이미지가 39px*39px이 아닌 50px*50px로 바뀐다면, S3 버킷에 저장된 썸네일 이미지는 모두 바뀐 정책에 따라 마이그레이션해야 합니다. 이미지 정책은 언제든 변할 수 있기에 우리는 업로드 시 필요한 이미지를 모두 저장하는 것이 아닌, 사용자 요청이 들어올 때마다 리사이징해서 내려주는 방법을 선택했습니다. 사용자 요청이 들어올..

    [SpringBoot] Logback 파일과 Docker 볼륨 연동하기

    스프링부트 서버를 띄울 때 로컬에선 콘솔로 에러 로그를 확인하면 되지만, 원격 EC2에 서버를 띄우면 에러 로그 확인이 어려워집니다. 장애 대응 시 에러 로그를 바로 확인하는 것이 중요하기에, 이번 글에서는 에러 로그를 어떻게 하면 바로 확인할 수 있는지 알아보고자 합니다. 모든 설명은 EC2에 도커 컨테이너 기반으로 서버를 띄우고, 로깅 프레임워크로 Logback을 사용한다는 가정하에 진행하겠습니다. Logback 적용 전 아직 Logback을 적용하지 않았다면 스프링부트 서버의 로그는 파일 형태가 아닌 표준 출력을 따를 것입니다. 따라서 docker logs 명령어를 통해 로그를 확인할 수 있습니다. docker logs 명령어는 컨테이너 내부에서 출력을 보여주는 명령어입니다. docker logs ..

    [PostgreSQL] json, jsonb 타입과 연산자

    데이터베이스 테이블 내 하나의 컬럼에 JSON 데이터를 저장하는 경우가 있습니다. 보통 외부에서 제공된 데이터를 별도의 처리 없이 그대로 저장할 때 JSON 타입으로 저장하게 됩니다. PostgreSQL은 JSON 데이터를 저장하기 위한 2가지 타입인 json과 jsonb 타입을 제공합니다. 1. json vs jsonb 타입 그렇다면 json과 jsonb 타입의 차이점은 무엇일까요? json 타입은 입력된 텍스트 원본을 저장하고, jsonb 타입은 decopmose된 바이너리 형식으로 저장한다는 차이가 있습니다. json 타입은 입력된 텍스트에 대한 정확한 복사본을 저장하기 때문에, 처리할 때마다 매번 파싱을 다시 해야만 합니다. jsonb 타입에 비해 처리 속도가 느리나, 원본 그대로를 저장할 수 있..

    코틀린 Backing Field와 Backing Properties

    "TDD, 클린 코드 with Kotlin 7기" 블랙잭 미션을 진행하다가 MutableList를 사용한 프로퍼티에서 backing-properties를 사용해 보라는 피드백을 받았다. 이번 기회에 코틀린에서 생소했던 개념인 Backing fields, Backing properties에 대해 알아보려고 한다. field vs property 필드와 프로퍼티의 차이점은 무엇일까? 공식 문서에 따르면 필드란 프로퍼티의 일부로서 단지 메모리에 값을 저장하기 위한 용도로만 사용하는 것을 말한다. In Kotlin, a field is only used as a part of a property to hold its value in memory. 자바에선 상태를 저장하기 위한 기본 개념이 필드이지만, 코틀린에서..

    Transactional Outbox 패턴을 이용한 메시지 발행의 신뢰성 보장

    부제: 메시지 발행의 신뢰성을 보장하기 위해선 어떻게 해야할까? Transactional Outbox 패턴이란 비즈니스 엔터티를 업데이트하는 행위와 메시지를 발행하는 행위 사이에 메시지를 저장하는 로직을 추가한 방법입니다. Transactional Outbox 패턴을 구현하는 방법은 다음과 같습니다. 메시지를 저장하는 Outbox 테이블을 만든다. 비즈니스 엔터티를 업데이트하는 로직과 메시지를 저장하는 로직을 하나의 트랜잭션으로 묶는다. Outbox 테이블을 풀링하는 별도의 프로세스를 통해 메시지를 발행한다. Transactional Outbox 패턴을 통해 트랜잭션이 커밋될 때만 메시지를 전송하도록 보장할 수 있습니다. 또한. 메시지를 발행하는 로직을 트랜잭션에서 완전히 분리하여 외부 메시지 브로커에 ..

    package-info.java란

    서론 팀 내 코드리뷰를 하다 패키지에 package-info.java 파일이 존재한다는 사실을 알게 되었습니다. package-info.java가 무엇인지 잘 몰랐기에 이번 기회에 알아보고자 합니다. package-info.java란 package-info는 모든 Java 패키지에 추가할 수 있는 Java 파일입니다. package-info.java 파일은 2가지 목적으로 사용됩니다. 패키지 단위의 문서 작성 패키지 단위의 애노테이션 적용 1) 패키지 단위의 문서 작성 JDK5 이전에는 package.html을 통해 패키지 단위의 Javadoc 코멘트를 body 태그 안에 작성했습니다. 그러나 JDK5 이후에는 package-info.java가 등장해 package.html의 역할을 대신하기 시작했습니다..

    Redis를 캐시 저장소로 사용할 때 고려할 점

    상황 팀 내에서 캐시 저장소로 레디스를 고려하고 있어, RedisCacheManager를 세팅하는 업무를 맡았습니다. 작년에 우테코 팀 프로젝트를 진행하면서 Refresh token을 레디스에 넣어 사용했던 적은 있었으나, 실무에서 레디스를 운영해 본 경험은 없었습니다. 따라서 토이 프로젝트 환경이 아닌 실무에서 레디스 환경을 구축하면서 고민했던 점을 이야기해 보고자 합니다. 로컬 캐시 vs 글로벌 캐시 글로벌 캐시를 도입하기 전에 로컬 캐시와 글로벌 캐시의 차이점에 대해 명확히 할 필요가 있습니다. 로컬 캐시는 서버의 인메모리에 저장하는 캐시로 외부 저장소를 이용하지 않기 때문에 빠르다는 장점이 있습니다. 그러나 서버마다 각기 다른 캐시를 저장하기 때문에 변경 사항이 생길 경우 동기화가 즉시 이루어지지..