본문 바로가기

개발/SpringBoot

(10)
Spring MVC, Spring Webflux SSE 성능 비교 개요많은 사용자 트래픽이 예상되는 프로젝트에서 SSE(Server-Sent Events)를 활용해 사용자에게 데이터를 스트림으로 전송해야 하는 요구사항이 생겼습니다. 이러한 환경에서 서버 성능 이슈로 SSE를 빠르게 전달하지 못하면 UX가 심각하게 나빠지기 때문에 효율적인 요청 처리가 무엇보다 중요합니다. 그렇기에 프로젝트를 시작하기 전, Spring Boot 환경에서 어떤 방식으로 SSE를 구현할지 고민했습니다.  Spring에서 SSE를 구현하려면 두 가지 방법이 있습니다.1. SseEmitter를 WebMVC에서 사용하는 방식2. WebFlux 환경에서 Flux를 사용하는 방식. 많은 사용자 요청을 효율적으로 처리해야 하기 때문에 두 가지 방법의 성능테스트 해보면서 더 나은 성능을 보이는 방법을 찾..
Redis + Spring AOP를 활용한 Rate Limiter 기능 구현 개요현재 진행중인 개인 프로젝트에선 Rate Limiter 기능이 적용되어있지 않습니다. 이 경우 만약, 악성 사용자가 서버를 다운시키기 위해 짧은 시간 내에 다량의 영상을 업로드할 경우, 서버에 심각한 부하가 발생할 수 있고, 한 영상에 매크로성 댓글이 여러 개 달리면 사용자 경험에 부정적인 영향을 줄 수 있습니다. 이러한 문제들을 예방하고자 Rate Limiter를 적용하기로 결정했습니다. Rate Limiter를 적용하기 전 고민이 있었는데요, Resilience4j, Bucket4j와 같이 기존에 존재하는 라이브러리를 사용해 구현할지, 아니면 Redis를 이용해 직접 구현할지 고민되었습니다. 라이브러리를 사용하면 구현이 간편하다는 장점이 있습니다. 하지만 스프링 애플리케이션 메모리를 직접 사용하기..
날짜별 조회 수 분석 기능 구현 개요티스토리 블로그관리 창에 들어가면 날짜 별 조회수를 나타내주는 아래와 같은 사진이 보입니다.   현재 진행중인 동영상 조회 플랫폼 Inugram에도 이 기능을 한번 적용해보겠습니다.    설계영상 조회수 기능을 설계하기 전, 스스로 세가지 조건을 고려하며 설계하였습니다. 1. 다중 사용자 조회 시 lost update 문제가 발생하면 안된다.2. 많은 사용자 트래픽을 수용할 수 있어야 한다.3. 사용자가 매 일자마다 조회수가 얼마나 집계되었는지 확인할 수 있어야한다. 일단 첫 번째 문제는 두 가지 방식으로 해결할 수 있습니다. 1. RDB에 락을 걸어서 lost update를 방지한다.2. 조회 수 저장소로 Redis를 사용해서 INCR 명령어로 원자적 연산을 수행한다. 만약 첫 번째 방법을 사용하게..
S3 presigned Url 적용을 통한 영상 업로드 성능 향상 서론숏츠 업로드 플랫폼을 개발하던 중, 업로드 기능의 성능을 테스트 해 보았습니다. 20MB 데이터를 가진 업로드 요청에 평균적으로 1초가 소요되었기 때문에, 동시 요청이 발생하면 가용 가능한 스레드가 존재하지 않아 에러가 발생할 것으로 예상했습니다.   테스트 (presigned URL 적용 전) 초기 서비스 대상은 교내 학생들이었기 때문에, 숏츠 업로드 API의 동시 요청을 10명으로 잡고 테스트했습니다.  초당 트랜잭션 처리량은 9.1 이었고, MTT는 979ms로 측정되었습니다. 눈여겨 봐야할 부분은 Errors 인데, 694개의 요청에 대해 201개로 34.5%의 에러 발생률을 기록했습니다.  10명이 동시에 비디오 업로드 API를 호출하면 최소 세명 이상은 에러를 반환받는다는 것 이기 때문에 ..
SpringDoc Swagger Https 설정법 문제 상황 기존 프로젝트를 멀티모듈로 전환하면서 Spring Boot 버전도 2 -> 3으로 올리게되었다. 스프링부트 3에서는 springfox swagger를 지원하지 않기 때문에 springdoc swagger로 전환하게 되었지만 해당 라이브러리는 springfox에서 사용하던 방식으로 https 세팅이 불가능했다. 해결 @OpenAPIDefinition의 위와 같이 url을 넣으면 스웨거 화면에서 기본 url이 세팅된다. servers 안에 여러개의 url 적용이 가능하기 때문에 로컬 환경을 위한 url도 삽입하면 테스트 시 용이하게 사용할 수 있을 것이다. reference https://stackoverflow.com/questions/70843940/springdoc-openapi-ui-how..
네이티브 쿼리문 + @SqlResultSetmapping 을 이용한 조회 문제 상황 최범균님의 도메인 주도 개발 시작하기 책을 읽으며 기존 프로젝트를 리팩토링 하던 중 문제에 직면했습니다. StudyEntity (study) 는 StudyApply 벨류 타입을 컬렉션으로 가지고 있으며 해당 벨류 타입을 별도 테이블로 (apply) 생성했습니다. 문제는 기능 요구사항 중 apply 테이블과 study 테이블을 조인해 값을 가져와야 하는 것이었습니다 JPQL에서는 컬렉션으로 지정된 StudyApply의 필드값에 접근할 수 없기 때문에 네이티브 쿼리문 + @SqlResultSetmapping을 이용해 문제를 해결했습니다. StudyEntity @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Table(name..
멀티 프로세스 환경 DB 동시성 제어 서론 현재 진행중인 StudyHub 프로젝트를 2월말 출시한 뒤 사용자 유치를 위해 이벤트를 계획하고 있었다. 이벤트 순서는 다음과 같다. 1. 이벤트 시간이되면 회원이 홈 화면에 있는 이벤트 참여 버튼을 누른다. 2. GiftStock 테이블의 StarBucks를 GiftName으로 가지는 튜플을 조회해 재고가 남아있는지 확인한다. 3. 재고가 남아있지 않을 경우 요청을 예외처리하고, 재고가 남아있을 경우 재고의 개수를 (남아있는 재고 - 1) 로 update 한다. 4. Gift 테이블에 회원과 선물 정보를 insert 한다. @Transactional public GiftStock register() { GiftStock giftStock = giftStockRepository.findByGiftN..
[StudyHub] unknown column '필드명' in 'field list' 에러 해결 서론 unkown column ~~ in field list 에러가 발생하기 하루 전, 엔티티의 테이블명, 컬럼 이름을 바꾸고 로컬에서 테스트를 마친 뒤 배포 서버에 배포했지만.. 게시글 전체 조회, 회원사진 저장, 회원 삭제 API가 정상적으로 동작하지 않는다는 프론트분의 메시지를 받았다. 로컬 테스트 환경에서는 잘 동작하는 것만 믿고 배포 환경에서 제대로 테스트해보지 못한게 불찰이었다. 흔들리는 멘탈을 부여잡고 팀원분과 함께 원인 분석을 해보았다. 문제 발생 스터디허브는 블루그린 배포 방식을 이용해 배포하기 때문에 main 브랜치에 커밋 할때마다 기존 서버가 종료되고 새로운 서버가 실행된다. 이 때 만약 ddl auto를 create로 잡게되면 매번 DB 테이블에 있는 튜플들이 초기화된다. 곧 서비스..
[StudyHub] 조회 메서드 반환 객체 분리 서론 프로젝트의 repository 레이어 단위테스트를 작성하던 중 개선사안 두가지를 발견했다. [개선 사안 1] 게시글 조회 관련 StudyPostRepositoryImpl 객체의 메서드들은 동적 쿼리 생성 시 아래 코드와 같이 반환값으로 각기 다른 dto를 반환하고 있었다. @Override public Slice findByInquiry(final InquiryRequest inquiryRequest, final Pageable pageable, Long userId) { QStudyPostEntity post = studyPostEntity; QUserEntity user = userEntity; QBookmarkEntity bookmark = bookmarkEntity; ...중략 @Overri..
[StudyHub] 시큐리티 의존성 삭제 2023 11월 22일 앱센터 15기 서버파트 스터디를 진행중, 앱센터 내 도서관리 시스템의 아키텍쳐를 구축하고 있었습니다. 앱센터 회원이 학교 이메일로 인증을 받아 로그인을 진행할 때, 스프링 시큐리티를 사용해 필터 단에서 처리해줄 지 직접 DB와 통신해 로그인을 처리할지 의논하던 중 불현듯 한가지 생각이 들었습니다. StudyHub에서 시큐리티를 왜 사용하고있지??.... 프로젝트를 처음 진행할 때를 생각해보면 정말 무지성으로 "로그인 하는데 스프링 시큐리티를 안써?? 무조건 써야지!" 라는 안일한 생각으로 시큐리티를 사용했었습니다. 이유없이 무지성으로 작성한 코드(기술)는 유지보수의 후폭풍을 몰고온다는 사실을 StudyHub 프로젝트에서 체감했기 때문에 StudyHub 프로젝트에서 시큐리티를 썼을때..