728x90
Java & Spring
54

[Nginx] event-stream(SSE), WebSocket의 지속 연결

문제Nginx는 기본적으로 Upstream으로 요청을 보낼때 HTTP/1.0 버전을 사용한다. 따라서 Nginx에서 WAS로 요청을 보낼 때 HTTP/1.0을 사용하여 Connection: close 헤더를 사용하게 된다. 하지만 SSE(Server Side Event) 또는 WebSocket은 지속 연결이 필요하기 때문에 HTTP/1.0을 사용하게되어 Connection 요청 후 바로 끊기게된다.해결SSE(Server Side Event)의 경우 다음과 같이 지속 연결이 될 수 있도록 Nginx에 Http Version과 Connection 헤더를 keep-alive 로 설정해주면 해결된다.proxy_set_header Connection 'keep-alive';proxy_http_version 1.1..

Java & Spring 2024.05.31

Message Broker를 사용하는 이유

SSE(Server-Sent Events)를 사용하여 서버에서 클라이언트로 알림 메시지를 전송할 경우 아래와 같이 단순하게 구현할 수 있다.이때 서버는 저장해둔 SseEmitter를 사용하여 클라이언트에게 메시지를 전송한다. 하지만 이후 서버가 추가되어 여러 대가 될 경우 다음과 같은 문제가 발생한다.클라이언트가 4개, 서버가 3대일 경우 다음과 같은 순서로 동작한다고 해보자.Client A는 Server A에게 SSE 연결을 요청Client A는 댓글 작성과 같은 이벤트 발생Server B에서 Client A의 이벤트를 처리Server B에서 Client A로 알림 메시지를 전송하기위해 SseEmitter를 조회Client A에 대한 SseEmitter는 Server A에 저장했지만 이벤트를 Serve..

Java & Spring 2024.05.21

Redis의 OOM(Out Of Memory)과 Memory 설정

메모리 확인Redis를 Standalone으로 Docker Container에 올리고 docker stats로 메모리를 확인해보았을 때 다음과 같았다.  그리고 로그인 후 유저 정보를 캐싱하고 캐싱한 키와 값이 메모리를 얼마나 사용하고 있는지 확인해보았다.  저장된 유저 정보마다 할당되는 크기가 다르겠지만 임의로 하나의 유저 정보가 사용하고 있는 메모리를 확인해보았을 때 1046byte(1.046kb) 만큼 사용하는 것을 확인할 수 있었다. ElastiCache의 데모 옵션(0.5 GiB)을 기준으로 계산해보면 약 50만명의 유저 정보를 저장할 수 있다. 하지만 Redis의 메모리는 데이터를 저장하는 것 이외에도 다양한 기능을 통해 사용되기 때문에 동시 접속 사용자가 50만명보다 적더라도 충분히 OOM(..

Java & Spring 2024.05.19

업데이트 쿼리에서 발생하는 TransactionRequiredException

@Modifying @Query("update Alarm a set a.readAt = now() where a.id = ?1") void updateReadAtById(Long alarmId);위와 같이 알람의 readAt 속성 값을 현재 일시로 업데이트하는 쿼리메소드를 실행하면 다음과 같은 예외가 발생한다. TransactionRequiredException: Executing an update/delete query@Transactional예외를 해결하기위해서 @Transactional을 추가해주면 된다.@Transactional@Modifying @Query("update Alarm a set a.readAt = now() where a.id = ?1") void updateReadAtBy..

Java & Spring 2024.05.17

Dive into @Transactional

SimpleJpaRepository먼저 Spring JPA에서 기본으로 구현된 SimpleJpaRepository 코드를 확인했다. SimpleJpaRespository 는 전체 메소드에 @Transactional(readOnly = true) 를 적용하고 save, delete를 수행하는 메소드에서는 @Transactional 에 적용한 것을 확인했다. 일반적으로 조회할 때 사용하는 메소드 findById, findAll , 저장할 때 사용하는 메소드 save 가 SimpleJpaRepository 에서 구현된 메소드를 호출한다는 사실도 알게되었다. 따라서 직접 @Transactional을 추가/적용하지 않아도 Transaction이 걸리는 것을 로그를 출력하여 확인했다.@Transactional을 직..

Java & Spring 2024.05.17

프론트엔드와 백엔드 연동 시 CORS 에러

프론트엔드(React) 애플리케이션을 실행하면 localhost:5173, 백엔드(Spring Boot) 애플리케이션을 실행하면 localhost:8080이 지정된다고 할 때 프론트엔드에서 백엔드로 API 요청을 하게되면 CORS(Cross-Origin Resource Sharing)에러가 발생한다.NOTECORS(Cross-Origin Resource Sharing) 에러는 서로 다른 두 서버 간의 Origin(Protocol + Hostname + Port)이 다를 경우 발생한다. CORS에러를 해결하기위해 다음과 같은 방법들을 적용할 수 있다.1. WebMvcConfigurer@Configuration @EnableScheduling @EnableTransactionManagement publ..

Java & Spring 2024.05.17

게시글의 좋아요를 업데이트할 때 발생할 수 있는 동시성 이슈

위는 ERD의 일부만 캡처한 이미지로 article에는 좋아요 수를 나타내는 like_count 속성이 있고 각 좋아요의 정보를 담는 article_like 테이블이 있다. 게시글의 좋아요를 업데이트하는 경우는 추가할 때, 혹은 삭제할 때 뿐이다. 당연히 기본 값은 0이 되어야할 것이다. 좋아요 추가 좋아요를 추가할 때를 기준으로 테스트를 해볼 것이기 때문에 추가하는 코드를 살펴보자. 편의를 위해 추가적인 메소드 코드와 엔티티 코드는 생략한다. @Transactional public void addArticleLike(Long articleId, Long userAccountId) { // 좋아요 추가 articleLikeRepository.save(ArticleLike.of(userAccountId, ..

Java & Spring 2024.03.20
728x90