사전적 의미 : ‘'(어디를 가서) ~을 가지고 오다’ → JPA에서도 같은 의미로 Fetch는 엔티티의 필드에 DB에서 실제 값을 가져오는 것이다.
2) 차이점
EAGER : 즉시 로딩으로 단어의 의미와 같이 예를 들어 User 엔티티에 OneToMany 관계로 Board가 설정되어 있으면, User 조회 시 연관된 모든 Board 데이터들도 가져오는 것이다. 이렇게 가져오게 되면 모든 연관된 Board 데이터들은 영속성 컨텍스트에 등록되고 관리된다.
문제점 : 만약, User의 nickName만 조회할 부분인데 EAGER을 쓰게 되면?? → 필요없는 쿼리를 발생시키고 메모리만 차지하게 되는 낭비가 발생
LAZY : 지연 로딩으로 자원이 실제로 쓰일 때까지는 요청하지 않고 있다가 실제로 필요할 때 요청하여 최대한 요청을 미루는 방식이다.
이렇게 설계를 하게 되면 필요없는 데이터를 메모리에 넣고 있을 필요 없이 연관 데이터가 필요한 순간에 쿼리를 발생 시킨다.
JPA에서는 LAZY 방식을 프록시 패턴으로 설계되어 있다.
프록시 패턴 : 어떤 객체에 대한 접근을 제어하는 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴
FetchType이 Lazy로 설정되었다면 프록시를 먼저 넣어 놓은 뒤 실제로 요청할 때 값을 가져온다는 것이다.
JPA 기본값
ToMany → LAZY Loading
ToOne → EAGER Loading
JPA의 ToOne에 해당하는 Fetch 타입은 Eager이다. 그렇기 때문에 ToOne인 매핑은 가능한 전부 LAZY로 설정하는 것을 권장
로딩 과정에서 문제점
N+1 문제
LAZY로 설정 후, 값을 가져오는 과정에서 문제가 발생한다.
N+1
ToMany 인 경우 1개 쿼리만 요청했는데 N만큼 쿼리가 추가로 발생하는 문제
→ JPQL 객체 지향 쿼리 언어로 해결 가능
LazyInitializationException
프로그램이 기동되면 각 엔티티들이 초기화되는데 LAZY 모드일 때 프록시 객체가 들어가 있다. 이때, 초기화 할 때는 EntityManager를 통해 프록시 객체를 넣어두었지만 초기화가 끝나면 그때는 EntityManager와의 연결이 끊어진 (준영속)상태이다. 즉, 초기화한 상태에서 EntityManager를 통해 연결하지 않은 상태로 프록시를 건드리게 되면 프록시 객체는 진짜 값들을 가져올 수 없는상태에 놓이고 이때 LazyInitializationException이 발생한다.
이때, EntityManager를 연결해 놓은 상태로 두면 되지 않나? 라고 생각할 수 있지만, 이는 모든 매핑된 값들이 연결된 상태가 되어 버린다. 즉, 트랜잭션이 계속 유지되고 있어 DB의 동시성 성능에 치명적인 영향을 주고 메모리를 차지하게 되므로매우 비효율적으로 동작하게 된다.
해결 방법
EAGER 사용?? 현재 프로젝트에서는 이렇게 사용하여 고쳐볼려 했지만 이치상 맞지 않음
@Transactional 사용?
메서드에 이 어노테이션을 적용하면 해당 메서드가 수행되는 동안은 세션이 계속 유지된다.
@Transactional을 사용하면 해당 메서드의 시작부터 끝까지 트랜잭션이 유지되어 위 예외가 발생하지 않는다. 그리고 메서드 수행 중 다른 @Transactional을 만나면 기본 전략으로 트랜잭션이 합쳐진다.
이 방법이 가장 간편하지만 동작이 복잡해질 경우 성능적으로 좋지 않다!!
이유
트랜잭션 내에서 데이터베이스 작업이 많이 실행되거나 오랜 시간이 소요되는 경우, 트랜잭션의 일관성을 유지하기 위해 데이터베이스 리소스가 장기간 점유될 수 있음 → 다른 트랜잭션들의 처리를 지연시키고 전체적인 성능을 저하시킴
lock 충돌이 발생할 수 있음
트랜잭션이 사용되는 동안 해당 데이터에 대한 락이 유지 So, 동시에 여러 개의 트랜잭션이 동일한 데이터를 수정하려고 할 때 락 충돌이 발생할 수 있음 → 이는 동시성 접근을 제한하고 대기 시간이 늘어나 성능에 영향을 줄 수 있음
JPQL?
JPQL의 JOIN FETCH를 사용하면 된다.
엔티티 그래프?
엔티티 그래프는 엔티티를 조회하는 시점에 연관된 엔티티를 함께 조회하는 기능이다.
가져온 후, 영속성 컨텍스트에 등록되어 있으면 다음부터는 엔티티 그래프는 적용되지 않는다.
Transaction
DB의 데이터를 수정하는 도중 예외가 발생할 경우 수정되기 전으로 롤백하기 위해 사용
더 이상 쪼갤 수 없는 최소 작업 단위
Spring 트랜잭션
사용하는 이유 : 트랜잭션을 쓰는 이유는 데이터 무결성(integrity) 때문
동기화
JDBC를 이용하는 개발자가 직접 여러 개의 작업을 하나의 트랜잭션으로 관리하려면 Connection 객체를 공유하는 등 상당히 불필요한 작업들이 많이 생길 것 → 이를 해결하기 위한 방법
해결 방안 : 트랜잭션을 시작하기 위한 Connection 객체를 특별한 저장소에 보관해두고 필요할 때 꺼내쓸 수 있도록 하는 기술
하지만 개발자가 JDBC가 아닌 Hibernate와 같은 기술을 쓴다면 위의 JDBC 종속적인 트랜잭션 동기화 코드들은 문제를 유발
Hibernate?
JPA라는 명세의 구현체 → JPA는 기술 스펙이고 하이버네이트는 이 기능을 구현하여 공급해주는 역할
자바 환경에서의 객체-관계 모델 매핑 솔루션
orm이라 불리우는 객체-관계 매핑은 어플리케이션레벨의 도메인 객체를 관계형 데이터베이스 테이블의 형태로 혹은 역으로 매핑시켜주는 프로그래밍 기술
추상화
애플리케이션에 각 기술마다(JDBC, JPA, Hibernate 등) 종속적인 코드를 이용하지 않고도 일관되게 트랜잭션을 처리할 수 있도록 해줌
AOP를 이용한 트랜잭션 분리
트랜잭션 코드와 비지니스 로직 코드가 복잡하게 얽혀있는 코드는 분리하는 것이 좋지만, 거의 불가능하다.
Spring에서는 마치 트랜잭션 코드와 같은 부가 기능 코드가 존재하지 않는 것 처럼 보이기 위해 해당 로직을 클래스 밖으로 빼내서 별도의 모듈로 만드는 AOP(Aspect Oriented Programming, 관점 지향 프로그래밍)를 고안 및 적용하게 되었고, 이를 적용한 트랜잭션 어노테이션(@Transactional)을 지원하게 됨.
3) hard delete와 soft delete
hard delete
SQL의 DELETE 명령어를 사용하여 직접 데이터를 삭제하는 방법
삭제 대상인 데이터가 필요 없을 때 (추후에 조회할 필요가 없을 때!!) 사용
soft delete
SQL의 UPDATE 명령어를 사용하여 삭제 여부를 알 수 있는 컬럼에 데이터가 삭제되었다는 값을 넣어서 표현
삭제를 해도 삭제하기 전의 데이터를 보관해야 할 경우에 논리 삭제를 사용
hard delete가 안좋은 이유
쿼리를 요청할 때, delete 요청은 다른 요청에 비해 비용이 훨씬 많이 듬. → Delete 쿼리는 데이터의 물리적인 삭제 작업과 함께 인덱스 갱신, 락 충돌, 트랜잭션 로그 등의 추가 작업을 수행해야 하므로 비용이 많이 들게됨.
#2. jpql과 쿼리dsl 차이점
1) 차이점
JPQL : JPA의 일부로 Query를 Table이 아닌 객체(=엔티티) 기준으로 작성하는 객체지향 쿼리 언어라고 정의할 수 있음
문제점
String 형태이기 때문에 개발자 의존적이다.
Compile 단계에서 Type-check 불가능
Runtime 단계에서 오류 발견
Spring Data JPA는 Spring에서 제공하는 라이브러리 중 하나로, JPA를 쉽고 편하게 사용할 수 있도록 도와줌
ex) findByUser(User user);
쿼리 DSL : 정적 타입을 이용해서 SQL, JPQL을 코드로 작성할 수 있도록 도와주는 오픈소스 빌더 API
특징
문자가 아닌 코드로 작성
Compile 단계에서 문법 오류 확인 가능
자동 완성 기능 활용 가능
동적 쿼리 구현 가능
2) 성능 차이
Querydsl은 JPQL을 코드로 만드는 것이기 때문에 결과적으로 JPQL로 실행되기 때문에 성능에는 거의 차이가 없다.
3) 쿼리 최적화를 진행한다면…
SELECT문을 보다 빠르고 효율적으로 실행되도록 하기 위한 것을 쿼리 최적화 작업
쿼리 최적화가 필요한 이유
한 번 데이터를 가져올 때 빠르게 가져오고 사용중이던 커넥션을 다른 쪽에서 사용할 수 있도록 빠르게 반환 해야 하기 때문!
쿼리 평가 엔진
SQL 구문을 분석하고 어떤 순서로 데이터에 접근할지를 결정 → 즉, 우리가 DB에 쿼리를 전달하게 되면 먼저 쿼리 평가 엔진의 파서를 통하게 된다.
파서 : 구문 분석 → 쿼리의 문법이나 없는 테이블, 컬럼 등이 작성되어 있으면 실행을 중단 시킴
파서를 통해 파싱된 쿼리는 옵티마이저로 전송
옵티마이저(최적화) : 파싱된 쿼리를 어떻게 실행하는 것이 가장 효율적일지 판단 → 실행계획 수립
인덱스 유무, 데이터 분산, 편향 정도, 매개변수 등의 조건을 고려해서 실행 가능한 계획을 수립하고 그 중 가장 낮은 비용의 실행 계획을 선택 ← 카탈로그 매니저가 제공
옵티마이저는 우리가 쿼리 최적화를 시킴에 있어서 많은 도움을 주지만 완벽하지는 않음. So, 쿼리를 작성하고 실행 계획을 보면서 쿼리를 수정하는 것이다.
수정이 필요한 이유 : 카탈로그 매니저가 제공하는 정보는 시간이 지나면서 데이터 변경이 이뤄지는 것을 갱신해야 하므로 쿼리를 수정해야 한다. 이때, SQL 구문이 너무 복잡하게 작성되어 있다면 최적의 계획을 선택하지 못 할 수도 있다.
실행 계획을 보는 법 : EXPLAN 명령어 사용 → 옵티마이저가 선택한 최적의 실행 계획을 보여줌
눈 여겨 봐야 할 영역
rows : 테이블에서 들여다보는 row의 갯수를 의미(작을 수록 좋음)
Extra : Using where, Using index 등등의 값이 있다. → where 같은 경우는 쿼리문 조건에 의한 필터가 이루어진 경우 index 같은 경우는 테이블의 설정된 인덱스를 타는 경우를 뜻한다.
대부분의 경우 인덱스를 타는 경우 비용적으로 유리한데 이는 모집합의 데이터가 많을 수록 인덱스 스캔이 유리하기 때문 But, 데이터 수가 많지 않을 때는 full scan이 유리하고 많을 때는 index scan이 유리하다
Extra에 어떤 값이 있는지 확인하고 index를 타지 않는 항목이 있다면 인덱스를 타도록 하여 쿼리를 수정해서 쿼리 최적화를 시킬 수 있다.
최적화 방법
SELECT * 사용 X
UNION과 DISTINCT를 같이 사용 X → UNION에는 중복 값을 제거하는 기능이 존재
Distinct와 Group By를 같이 사용 X → Group By절에 입력된 칼럼을 그룹화
서브 쿼리에서 Order By를 사용 X → 브 쿼리(Sub Query)에서 Order By를 사용하는 경우 많은 비용이 발생
서브 쿼리의 결과가 많으면 EXISTS가 나은 성능을 제공하며 그렇지 않으면 IN을 사용하는 것이 좋음 → EXISTS는 일치하는 항목이 발견되는 즉시 검색 프로세스를 종료하지만 IN은 모든 항목을 비교하기 때문
#3. Restful 특징
Restful : REST API를 제공하는 웹 서비스를 RESTful 하다고 할 수 있다.
Rest : HTTP를 기반으로 필요한 자원에 접근하는 방식을 정해놓은 아키텍처
웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있다.
속성
서버에 있는 모든 자원은 각 자원 당 클라이언트가 바로 접근할 수 있는 고유 URI가 존재
모든 요청은 클라이언트가 요청할 때마다 필요한 정보를 주기 때문에 서버에서 세션 정보를 보관할 필요가 없다.(stateless)
HTTP 메서드를 사용 → HTTP 인터페이스인 GET, POST, PUT, DELETE 등의 메서드로 접근되어야 한다.
서비스 내에 하나의 자원이 주변에 연관된 자원들과 연결되어 표현이 되어야 한다.
구성
자원 : 고유한 ID가 존재하고, 이 자원은 서버에 존재
ex) .../feeds/{photoId} -> feeds 아래 {photoId} 자원
메서드 : HTTP 메서드를 사용 → GET, POST, PUT, DELETE 등이 존재
GET : 해당 리소스를 조회한다.
POST : 해당 리소스를 생성한다
PUT : 해당 리소스를 수정한다.
DELETE : 해당 리소스를 삭제한다.
→ PUT과 POST의 차이
멱등성
POST: POST 요청은 멱등성을 가지지 않음 → 동일한 POST 요청을 여러 번 보내면 서버는 각각의 요청마다 새로운 리소스를 생성하게 됨
PUT: PUT 요청은 멱등성을 가짐 → 동일한 PUT 요청을 여러 번 보내더라도 동일한 리소스 상태가 유지
리소스의 위치
POST: POST 요청은 서버에 리소스를 제출할 때 서버가 자체적으로 리소스의 위치를 결정 → 서버는 새로운 리소스를 생성하고 해당 리소스의 위치를 클라이언트에 응답으로 반환
PUT: PUT 요청은 클라이언트가 명시적으로 리소스의 위치를 지정 → 클라이언트는 PUT 요청을 통해 리소스를 특정 위치에 업데이트 또는 생성
안전성
POST: POST 요청은 일반적으로 서버의 상태를 변경하거나 부작용을 초래할 수 있음
PUT: PUT 요청은 일반적으로 리소스의 업데이트 작업을 수행하므로, 안전성을 가질 수 있음
메세지 : HTTP header, body, 응답 상태 코드 등으로 구성되어 있다.
즉, header에는 body에 어떤 형식으로 데이터가 담겼는지 표시하고, body는 자원에 대한 정보를 JSON, XML 등으로 전달. 응답 상태 코드는 200~500 사이의 숫자로 클라이언트의 요청에 대한 상태를 나타내 줌.
특징
Server-Client 구조
자원이 있는 쪽이 서버, 자원을 요청하는 쪽이 클라이언트
Stateless
HTTP 프로토콜은 기본적으로 무상태이므로 Rest 역시 무상태
무상태란 클라이언트의 상태(State)를 서버에 저장하지 않는다는 뜻
Cacheable
HTTP의 캐싱 기능을 적용할 수 있다.
계층화
클라이언트는 REST API 서버만 호출
#4. S3와 Lambda
Lambda의 원리
#4. jmeter의 과부화 테스트
#5. 테스트 코드
1) Junit이란…
TDD 형식으로 작성한다면…
서버 통신 이슈
#1. MongoDB
NoSQL(비관계형 데이터베이스)를 사용 이유
확장성: 채팅 서비스는 대규모의 동시 접속자를 처리해야 할 수도 있다. NoSQL 데이터베이스는 수평 확장성을 제공하여 필요에 따라 서버를 추가하거나 분산 시스템을 구축할 수 있다. 이는 데이터베이스의 성능과 처리 능력을 향상시키는 데 도움이 된다.
유연성: 채팅 데이터는 종종 구조가 복잡하고 다양한 형식일 수 있다. NoSQL 데이터베이스는 스키마가 없거나 유연한 스키마를 가지고 있어 데이터 모델을 자유롭게 변경할 수 있다. 이는 새로운 필드나 속성을 추가하거나 데이터 구조를 수정하는 데 편리하다.
실시간 처리: 채팅 서비스에서는 실시간으로 메시지를 송수신하고 처리해야 한다. NoSQL 데이터베이스는 분산 아키텍처와 메모리 기반 저장소를 활용하여 높은 처리량과 낮은 지연 시간을 제공할 수 있다. 이는 채팅 메시지의 실시간 전송과 응답을 지원하는 데 도움이 된다.
활용성: 채팅 데이터는 대부분 개별 메시지 단위로 발생하고, 사용자, 대화방, 타임스탬프 등 다양한 쿼리 요건이 있을 수 있다. NoSQL 데이터베이스는 쿼리의 유연성과 확장성을 통해 이러한 요구 사항을 처리할 수 있다. 또한 NoSQL 데이터베이스는 대용량 데이터 처리에 특화되어 있으므로 채팅 기록이 많은 경우에 유용하다.
가용성: 채팅 서비스는 항상 사용 가능해야 하므로 데이터베이스의 가용성이 매우 중요. NoSQL 데이터베이스는 데이터 복제, 자동 장애 복구 및 분산 환경에서의 운영을 지원하여 고가용성을 제공할 수 있다.
즉, NoSQL 데이터베이스는 이러한 이점들을 통해 채팅 서비스의 성능, 확장성, 유연성, 실시간 처리 및 가용성을 향상시킬 수 있다. But, 데이터 모델링 및 쿼리 작성 방식에서 기존의 관계형 데이터베이스와 차이가 있으므로, 적절한 선택과 설계가 필요.
만약 관계형 DB를 사용하게 된다면..?
유연성 : NoSQL은 스키마를 사전에 정의하지 않고 유연하게 데이터를 저장할 수 있는 반면, 관계형 데이터베이스는 정해진 스키마를 준수해야 한다. So, 채팅 데이터의 구조 상 비관계형 DB가 더욱 적합하다.
확장성 : 관계형 DB는 수평적 확장보다 수직적 확장을 지원한다. So, 대규모 채팅 애플리케이션에서 트래픽이 많아지면 데이터베이스 서버의 성능 한계에 도달할 수 있다. 이 때문에 NoSQL을 사용함 으로써 분산 DB Architecture를 기반으로 수평적 확장을 통해 대용량의 데이터와 트래픽을 처리하는 데 더 적합할 수 있다.
속도 : 관계형 DB는 탐색과 업데이트가 빠른 반면, NoSQL은 insert가 빠르기에 채팅 서비스는 insert 비율이 1:1이기 때문에 NoSQL이 더 적합하다.
#2. Redis
Redis는 인메모리 데이터 스토어로 분산 캐싱, 세션 관리, 실시간 분석, 메시지 브로커 등 다양한 용도로 사용된다.
사용 이유
높은 성능: Redis는 데이터를 메모리에 저장하여 빠른 읽기 및 쓰기 성능을 제공. 따라서 빠른 데이터 접근이 필요한 애플리케이션에서 많이 사용. 또한 Redis는 단일 스레드 모델을 사용하여 데이터 처리를 순차적으로 수행하므로 락 관리에 대한 복잡성이 줄어들어 성능이 향상.
데이터 구조 지원: Redis는 다양한 데이터 구조를 지원한다. 문자열, 해시, 리스트, 세트, 정렬된 세트 등 다양한 데이터 형식을 저장하고 조작할 수 있다. 이는 간단한 데이터 구조부터 복잡한 데이터 모델까지 다양한 용도로 활용할 수 있다는 장점을 제공한다.
in-Memory 형식이 아닌 DB 저장을 한다면…?
속도 : in-Memory는 데이터를 RAM에 저장하기 때문에 디스크 I/O가 필요하지 않음 So, 디스크에 접근하는 것보다 RAM에 데이터를 읽고 쓰는 속도가 훨씬 빠름.
인덱싱 및 쿼리 최적화 : 인 메모리 데이터 저장소는 인덱싱 및 쿼리 최적화를 위한 메모리 내 구조를 사용할 수 있음 So, 데이터를 인덱싱하고 쿼리를 실행하기 위한 데이터 구조를 메모리 내에 유지함으로써 데이터베이스 쿼리의 실행 속도를 향상시킬 수 있음 → 빠른 검색 및 조회 성능을 제공할 수 있음
in-Memory와 DB에 저장될 데이터 구분
in-Memory
자주 access가 필요한 데이터
임시 데이터
이러한 데이터는 일시적으로 필요하지만 오랫동안 보관할 필요가 없으며, 반복적인 액세스 시 매번 계산하는 비용을 줄일 수 있음
적은 용량의 데이터
DB
지속성이 필요한 데이터
데이터베이스는 데이터의 안정적인 보관과 백업을 제공하여 데이터 손실의 위험을 줄일 수 있음
동시 액세스가 필요한 데이터
여러 사용자 또는 여러 시스템에서 동시에 액세스해야 하는 데이터는 데이터베이스에 저장하여 동시성 문제를 관리할 수 있음
그러면, In-Memory는 동시성 관리를 어떻게 하나?
보안이 중요한 데이터
민감한 정보나 중요한 데이터는 데이터베이스의 보안 기능을 활용하여 액세스 제어와 데이터 암호화를 보장할 수 있음
#3. 웹 소켓
웹소켓은 실시간 양방향 통신을 제공하는 프로토콜
사용 이유
실시간 업데이트: 웹소켓은 서버와 클라이언트 간에 지속적인 연결을 유지하며, 양방향 통신을 가능하게 한다. 이를 통해 서버에서 클라이언트로 데이터를 실시간으로 업데이트하거나 클라이언트에서 서버로 요청을 보내고 실시간으로 결과를 받을 수 있다. 이는 실시간 채팅의 애플리케이션에 적합합니다.
효율적인 통신: 웹소켓은 HTTP 프로토콜과 달리 상태를 유지하며 연결을 유지하기 때문에, 각 요청마다 새로운 연결을 맺어야 하는 오버헤드가 없다. 이는 요청과 응답 사이에 작은 데이터 패킷을 주고받을 때 특히 유용하며, 데이터 전송의 지연 시간과 대역폭 사용량을 줄일 수 있다.
서버 푸시 기능: 일반적인 HTTP 요청은 클라이언트에서 서버로 요청을 보내는 방식이다. 그러나 웹소켓은 서버에서 클라이언트로 데이터를 프로액티브하게 푸시할 수 있는 기능을 제공. 이를 통해 서버에서 변경된 데이터를 실시간으로 클라이언트에게 전달할 수 있으며, 사용자 경험을 개선하고 서버와의 효율적인 상호작용을 가능케 한다.
단일 연결 유지: 웹소켓은 단일 연결을 유지하므로, 여러 개의 HTTP 요청 및 응답을 처리하는 데 필요한 네트워크 및 서버 자원을 절약할 수 있다. 이는 서버의 확장성과 성능을 향상시킬 수 있는 장점을 제공한다.
크로스 플랫폼 지원: 웹소켓은 다양한 클라이언트 플랫폼과 웹 브라우저에서 지원되는 표준 프로토콜이다. So, 웹, 모바일 앱, 데스크톱 앱 등 다양한 플랫폼 간에 일관된 실시간 통신을 구현할 수 있다.
웹 소켓의 네트워크 통신 프로토콜 흐름
HandShake
클라이언트가 서버에 웹 소켓 연결을 요청할 때, 일반적으로 HTTP를 통해 이루어지는데 이때 HTTP의 업그레이드 요청과 응답을 통해 웹 소켓 연결로 전환된다. → 클라이언트는 WebSocket 핸드셰이크 요청 헤더에 'Upgrade' 필드를 포함시켜 웹 소켓 연결을 요청하고, 서버는 핸드셰이크 응답으로 '101 Switching Protocols' 상태 코드를 반환하여 연결을 승인
연결 설정
핸드셰이크가 완료 후, 클라이언트와 서버는 양방향 통신을 위한 웹 소켓 연결이 설정 → 이후부터는 TCP/IP 기반의 지속적인 연결이 유지되며, 양방향 데이터 교환이 가능해짐
양방향 통신
양측은 독립적으로 데이터를 송수신 할 수 있으며, 전송된 데이터는 실시간으로 처리
단점
수평적 확장의 단점이 존재
웹소켓 서버가 일반 서비스 애플리케이션과 붙어있을 경우 , 수평적 확장(Scale Out)이 어렵다. So, 서비스 규모에 따라 웹소켓 서버를 모듈화 해서 일반 서비스와 구분 지어 사용하는 방식도 고려해봐야 함
#4. SSE
SSE(Server-Sent Events)는 웹에서 서버로부터 실시간으로 이벤트를 수신하는 기능을 제공하는 웹 표준
사용 이유
실시간 업데이트: SSE는 서버로부터 실시간으로 데이터를 받아와 클라이언트 측에서 업데이트할 수 있다. 이를 통해 서버에서 클라이언트로 데이터를 주기적으로 전송하고, 클라이언트는 이벤트를 캐치하여 UI를 업데이트할 수 있다. 이는 실시간 채팅, 알림등 실시간 정보가 필요한 애플리케이션에 적합하다.
단방향 통신: SSE는 클라이언트에서 서버로의 단방향 통신을 지원. 즉, 클라이언트는 서버로 요청을 보내지 않고도 서버로부터 데이터를 수신할 수 있다. 이는 서버의 푸시 기능을 활용하여 서버에서 변경된 데이터를 클라이언트에게 전송할 때 유용하다.
간편한 구현: SSE는 HTML5 표준으로 지원되며, 일반적으로 사용되는 웹 기술인 JavaScript와 함께 사용할 수 있다. So, 별도의 라이브러리나 프레임워크 없이도 상대적으로 간편하게 구현할 수 있다.
크로스 브라우저 호환성: SSE는 대부분의 최신 웹 브라우저에서 지원된다. 이는 다양한 플랫폼과 브라우저에서 일관된 방식으로 실시간 통신을 구현할 수 있음을 의미.
연결 유지: SSE는 서버와의 연결을 유지하면서 데이터를 전송할 수 있다. So, 클라이언트와 서버 간에 지속적인 연결을 유지하는 WebSocket과는 달리, HTTP 연결을 유지하는 SSE는 서버의 확장성과 성능을 고려할 때 유용할 수 있다.