“ResponseDTO 만들 때마다 Entity 변환 로직을 반복하고 있어. 이걸 효율적으로 정리할 수 있을까?”
“대용량 데이터는 Entity를 거치지 말고 바로 DTO로 받는 게 좋지 않을까?”
“RequestDTO는 팩토리 메서드까지 써야 할까, 아니면 단순하게도 충분할까?”

이런 고민을 해결하는 열쇠가 바로 DTO 객체 생성 전략입니다.


DTO 객체 생성 전략이란?

DTO 생성 방식을 상황에 따라 체계적으로 선택하는 설계 원칙입니다.
Entity와 DTO 간의 책임을 명확히 분리하면서 불필요한 변환 반복을 제거하고,
성능·가독성·협업의 균형을 잡는 방법론이기도 합니다.

  • Entity는 영속성 계층(Persistence Layer)을 책임지고,
  • DTO는 표현 계층(Presentation Layer)의 데이터를 담당합니다.

예를 들어,

  • 사용자 정보 조회 시 날짜 포맷 변경이나 민감정보 제거가 필요하다면 → 팩토리 메서드 사용
  • 단순 통계 데이터나 리스트만 빠르게 가져올 때는 → JPA Projection 활용이 효과적입니다.

상황별 DTO 생성 전략


1. 응답 DTO - 팩토리 메서드 + 빌더

  • 데이터 가공이 필요한 응답에 적합
  • 민감정보 제거, 날짜 포맷팅, Enum 변환 등 전처리 포함 가능
public class UserResponseDto {
    private Long id;
    private String username;
    private String email;
    private String createdAt;
    
    public static UserResponseDto fromEntity(User user) {
        return UserResponseDto.builder()
            .id(user.getId())
            .username(user.getUsername())
            .email(user.getEmail())
            .createdAt(user.getCreatedAt().toString()) // 날짜 변환
            .build(); // 비밀번호 등 민감정보는 제외
    }
}

 

💡 추가 팁:

  • 여러 필드에서 동일한 전처리가 필요할 경우, Converter 클래스로 분리해도 좋습니다.

2. JPA Projection - 성능 최적화

  • 대용량 조회나 대시보드처럼 읽기 전용 데이터에 최적화
  • Entity 생성을 생략해 메모리·네트워크 효율 증가
// DTO
@Getter @AllArgsConstructor
public class UserSimpleDto {
    private String username;
    private String email;
    // 팩토리 메서드 불필요 - JPA가 직접 생성
}

// Repository
@Query("SELECT new com.example.UserSimpleDto(u.username, u.email) FROM User u WHERE u.id = :id")
UserSimpleDto findSimpleDtoById(@Param("id") Long id);

⚠️ 주의사항: Projection 결과는 영속성 컨텍스트에 포함되지 않기 때문에 수정 불가입니다, 조회 전용 + 필드 수 적으면 무조건 Projection이 효율적입니다


3. 요청 DTO - 간단 생성자 or 빌더

  • 클라이언트 입력 데이터를 받는 용도로 사용
  • 복잡한 팩토리 메서드 불필요, 프레임워크(Jackson 등)가 자동 매핑
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SignupRequestDto {
    @NotBlank
    private String username;

    @NotBlank @Email
    private String email;
}
 

💡 : @Valid와 함께 Spring Validation을 사용, 요청 DTO는 최소한의 코드로 유효성 검증만 — 로직 넣지 마세요!


4. 복합 응답 DTO - 정적 팩토리 메서드

  • 여러 Entity나 외부 API 데이터를 조합해야 할 때 사용
  • 변환 로직을 DTO 내부에 숨겨 서비스 로직 간결화
public class ApiResponse<T> {
    private boolean success;
    private T data;
    private String message;

    public static <T> ApiResponse<T> success(T data, String message) {
        return new ApiResponse<>(true, data, message);
    }

    public static <T> ApiResponse<T> error(String message) {
        return new ApiResponse<>(false, null, message);
    }
}

💡 실무에서는 프론트와 공통 스펙으로 맞추는 데 유용 (status, data, message 구조 등)


⚖️ 실무 고민과 트레이드오프

성능 vs 유지보수성

전략장점단점
팩토리 메서드 일관성, 캡슐화, 유지보수 용이 약간의 성능 오버헤드
JPA Projection 성능, 메모리 효율 수정 불가, 유연성 부족
 
  • 대부분의 일반 CRUD에서는 팩토리 메서드가 안정적
  • 조회 성능이 중요한 화면에서만 Projection 전략 선택

언제 JPA Projection을 쓸까?

  • 필요한 필드가 Entity의 30% 이하
  • 조회 전용 데이터 (수정 또는 영속화 필요 없음)
  • 대량 리스트 or 통계 조회 시 효과적

팀 협업 시 네이밍 컨벤션 예시

목적메서드명
Entity → DTO fromEntity()
성공 응답 success()
에러 응답 error()
 

📌 네이밍 규칙 통일은 리뷰 속도 향상 + 온보딩 학습 곡선 완화


사고 팁: "DTO 전략은 선택지가 아니라 트레이드오프다"

AI 시대에는 “모든 문제를 툴로 해결하는 것”이 아니라,
어떤 전략이 어느 조건에서 최적이 되는지를 선택하는 판단력이 중요합니다.

→ 이것이 문제 분해 + 조건 분기 사고의 핵심입니다.


요약: 실무에서 DTO 생성 전략 선택 가이드

상황전략장점
복잡한 응답 데이터 팩토리 메서드 가독성 + 유지보수
단순 조회, 대량 데이터 JPA Projection 성능 향상
클라이언트 요청 수신 빌더 or 생성자 간결성, 자동 매핑
복합 응답 구조 정적 팩토리 메서드 재사용성, 명확한 구조화
 

필요에 따라 선택하되, 코드 일관성과 협업 효율을 기준으로 팀 기준을 세워보세요.
DTO 생성 전략은 단순한 기술 포인트가 아니라, 설계 의사결정의 집합입니다.

 

요약 및 결론

 

1. 응답 DTO (Response DTO): API 계약의 핵심

  • 동작 원리: JPA가 DB에서 가져온 엔티티 객체에서 클라이언트에게 필요한 일부 필드만 선택(선택적)하고, 민감 정보 제거 및 데이터 가공(예: 타입 변환)을 거쳐 응답 DTO를 만듭니다.
  • 최적 전략: 팩토리 메서드를 통한 객체 생성 중앙화(UserResponseDto.fromEntity(user))를 기본으로 하며, 내부적으로 Lombok @Builder를 사용해 DTO 필드를 초기화합니다.
  • 핵심 이유 (팩토리 메서드의 이점):
    • 1. 엔티티로부터의 안전한 변환: 엔티티의 민감 정보(비밀번호 등)를 자동으로 제거하고, 날짜 포맷 변경 등 복잡한 데이터 가공 로직을 DTO 내부에 캡슐화하여, UserResponseDto.fromEntity(user)처럼 안전하고 예측 가능한 DTO 생성 경로를 제공합니다.
    • 2. 여러 엔티티 데이터 조합: PostWithAuthorDto.of(post, user)와 같이 여러 엔티티(예: 게시글과 작성자)의 데이터를 조합하여 하나의 DTO를 만들 때, 복잡한 조합 로직을 팩토리 메서드 안에 깔끔하게 담아 코드의 응집도를 높입니다.
    • 3. 일관된 응답 규칙 강제 (여러 곳에서 사용 시): ApiResponse.success(data, message)처럼 성공/실패와 같은 표준화된 응답 구조를 여러 API에서 일관되게 사용하도록 강제하여, 클라이언트가 서버 응답을 예측하고 처리하기 쉽게 만듭니다. 빌더를 통한 유연한 필드 설정과 더불어, 팩토리 메서드는 엔티티 필드 중 필요한 것만 선택적으로 설정하여 DTO를 구성할 수 있게 합니다.

2. JPA Projection (성능 최적화):

  • 동작 원리: 엔티티 객체 생성을 건너뛰고, DB에서부터 필요한 컬럼만 직접 가져와 DTO 또는 인터페이스 형태로 바로 매핑합니다.
  • 최적 전략: 대량 데이터 조회필드 30% 이하의 극히 일부만 필요하며 순수 조회 목적일 때만 사용합니다.
  • 핵심 이유: 엔티티 객체 생성으로 인한 오버헤드를 없애고, DB 접근 성능을 극대화하여 리소스 사용을 효율화합니다.

3. 요청 DTO (Request DTO):

  • 동작 원리: Spring의 @RequestBody가 클라이언트의 JSON 요청을 DTO의 필드에 **자동으로 매핑(역직렬화)**합니다.
  • 최적 전략: Lombok 어노테이션(@Getter, @Builder 등)과 유효성 검증 어노테이션(@NotBlank 등)만으로 간결하게 정의합니다.
  • 핵심 이유: 프레임워크가 객체 생성을 담당하므로, 개발자가 별도의 생성 로직(팩토리 메서드 등)을 추가할 필요가 없어 코드를 단순화하고 개발 효율성을 높입니다.

'spring' 카테고리의 다른 글

[Spring] @Transactional  (2) 2025.06.15
HTTP와 RESTful API  (1) 2025.06.06

[DB] 트랜잭션의 기본 이해 에서 소개한 선언적 트랜잭션 관리의 핵심인 @Transactional 어노테이션을 정리합니다.

 

 

@Transactional은 메서드나 클래스에 선언하여 해당 범위를 트랜잭션으로 관리하도록 지시하는 Spring의 어노테이션입니다.

어노테이션 하나로 복잡한 트랜잭션 관리를 Spring이 자동으로 처리해줍니다. 마치 자동차의 자동변속기처럼, 개발자는 트랜잭션 세부 제어를 Spring에게 위임하고 비즈니스 로직에만 집중할 수 있습니다.

실제 서비스에서는 주문 처리, 회원 가입, 데이터 동기화 등 데이터 일관성이 중요한 모든 작업에 활용됩니다.

핵심 속성 상세 설명

1. propagation (전파 속성)

메서드 간 트랜잭션이 어떻게 전파되고 관리될지 결정하는 속성입니다.

트랜잭션이 이미 존재할 때 새로운 트랜잭션을 어떻게 처리할지 정의합니다. 부모 메서드에서 자식 메서드를 호출할 때의 트랜잭션 경계를 결정합니다.

 

주요 전파 레벨 

  • REQUIRED (기본값): 기존 트랜잭션 참여, 없으면 새로 생성
  • REQUIRES_NEW: 항상 새로운 트랜잭션 생성 (독립적 실행)
  • SUPPORTS: 트랜잭션 있으면 참여, 없어도 실행

💻 전파 속성 코드 예시

@Service
public class OrderService {
    
    // 기본 전파 레벨 (REQUIRED)
    @Transactional
    public void processOrder(Order order) {
        saveOrder(order);
        updateInventory(order);
        // 하나라도 실패하면 전체 롤백
    }
    
    // 독립적인 트랜잭션 (REQUIRES_NEW)
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOrderHistory(Order order) {
        // 주문 처리 실패해도 로그는 저장됨
        historyRepository.save(new OrderHistory(order));
    }
}

 

'spring' 카테고리의 다른 글

DTO 객체 생성, 상황별 최적 전략은?  (1) 2025.06.18
HTTP와 RESTful API  (1) 2025.06.06

온라인 쇼핑몰에서 상품을 주문했는데 결제는 완료되었지만 재고는 차감되지 않았다면 어떨까요? 아니면 포인트만 적립되고 주문 내역은 저장되지 않는다면?

 

이런 문제를 해결하는 것이 바로 트랜잭션입니다.

 

트랜잭션이란?

트랜잭션은 여러 개의 데이터베이스 작업을 하나의 논리적 단위로 묶어서, 모두 성공하거나 모두 실패하도록 보장하는 기능입니다.

  • 논리적 트랜잭션: 애플리케이션 관점에서 데이터 일관성을 유지하기 위해 함께 처리되어야 하는 일련의 데이터베이스 연산들의 묶음
  • 물리적 트랜잭션: 실제 DB에서 커넥션을 통해  '커밋/롤백이 일어나는 '최소 동작 단위' 입니다.

 

예시) 쇼핑몰 주문 과정을 생각해보세요. 재고 차감, 주문 저장, 결제 처리, 포인트 적립이 모두 성공해야 완전한 주문이 됩니다. 트랜잭션은 이 4가지 작업을 하나로 묶어서, 하나라도 실패하면 모든 작업을 취소시킵니다.

주문 트랜잭션 = {
  1. 재고 차감 (-1)
  2. 주문 저장
  3. 결제 처리  
  4. 포인트 적립
}

 

트랜잭션의 핵심 특징

1. Commit과 Rollback

Commit은 모든 작업이 성공했을 때 최종 확정하는 명령이고, Rollback은 문제 발생 시 모든 작업을 취소하는 명령입니다.

Commit 후에는 모든 사용자가 변경된 데이터를 조회할 수 있게 됩니다. Rollback 시에는 트랜잭션 시작 전 상태로 완전히 복구됩니다.

💻 코드 예시

BEGIN TRANSACTION;
UPDATE products SET stock = stock - 1 WHERE id = 1;
INSERT INTO orders (user_id, product_id) VALUES (123, 1);
-- 문제 발생 시
ROLLBACK;  -- 모든 변경사항 취소
-- 성공 시  
COMMIT;    -- 모든 변경사항 확정

2. ACID 원칙

원자성 (Atomicity)

모든 작업이 성공하거나 모든 작업이 실패하는 것을 보장합니다.

원자성이 보장되지 않으면 어떤 작업은 성공하고 어떤 작업은 실패해서, 어디서 무엇이 잘못됐는지조차 파악하기 어려워집니다.

일관성 (Consistency)

데이터가 정의된 제약 조건을 항상 만족해야 함을 의미합니다.

실무에서는 데이터베이스 제약조건과 함께 애플리케이션 레벨에서 비즈니스 규칙을 검증하여 일관성을 보장합니다. 예를 들어 계좌 잔액이 음수가 되지 않도록 하는 것이 일관성 유지입니다.

격리성 (Isolation)

동시에 실행되는 트랜잭션들이 서로 영향을 주지 않도록 격리하는 성질입니다.

모든 트랜잭션을 순서대로 처리하면 안전하지만 성능이 떨어집니다. 그래서 시스템 요구사항에 따라 적절한 격리 수준을 선택해야 합니다.

 

💻 격리 수준별 특징

READ UNCOMMITTED: 커밋되지 않은 데이터도 읽어 가장 빠르지만, 유령 같은 잘못된 정보(Dirty Read)를 볼 위험이 있습니다.
READ COMMITTED: 커밋된 데이터만 읽어 Dirty Read는 막지만, 반복해서 읽을 때마다 값이 달라질 수 있는(Non-Repeatable Read) 가능성이 있습니다.
REPEATABLE READ: 같은 트랜잭션 내에서 동일한 데이터의 값을 반복적으로 보장하지만, 새로운 레코드 추가/삭제(Phantom Read)로 인해 쿼리 결과 집합이 달라질 수 있습니다.
SERIALIZABLE: 모든 동시성 문제를 완벽히 막아 가장 안전하지만, 트랜잭션을 직렬로 처리하여 가장 느립니다.

 

지속성 (Durability)

트랜잭션이 성공적으로 커밋되면 그 결과가 영구적으로 보존되는 성질입니다.

시스템 장애가 발생해도 커밋된 데이터는 로그를 통해 복구가 가능합니다.

서로 다른 소프트웨어 시스템이 어떻게 대화할 수 있을까? REST API 개념 완벽 정리

 

 

온라인 쇼핑몰에서 결제 버튼을 누르면 어떻게 즉시 재고가 차감되고 배송 시스템이 동작할까요? 유튜브에서 좋아요를 누르면 어떻게 실시간으로 카운트가 올라갈까요?

서로 다른 시스템들이 이렇게 매끄럽게 협력할 수 있는 이유는 바로 REST API라는 공통 언어 덕분입니다.

 

 

헷갈리기 쉬운 개념들, 한 번에 정리

HTTP, REST, API, RESTful - 정확한 구분

HTTP는 인터넷에서 데이터를 주고받는 핵심 프로토콜입니다. 마치 우편 시스템처럼 편지(데이터)를 어떻게 배달할지 정한 규칙이죠.

API서로 다른 소프트웨어 시스템이 정보를 교환하고 기능을 수행할 수 있도록 하는 인터페이스입니다. 은행 ATM이 고객과 은행 시스템을 연결해주는 것처럼, API는 다양한 시스템 간의 소통을 가능하게 합니다.

REST는 HTTP가 가진 본질적인 특성(특히 무상태성, 균일한 인터페이스)을 최대한 존중하고 그 위에 건축된 아키텍처 스타일입니다. 2000년 로이 필딩이 "웹의 장점을 최대한 살리면서 확장 가능한 시스템을 만들려면 어떻게 해야 할까?"라는 고민에서 탄생했습니다.

RESTful은 REST 원칙을 충실히 따르는 시스템을 말합니다. REST API 중에서도 특히 원칙을 잘 지킨 '모범생' 격이죠.

 

 

HTTP의 핵심 - 무상태성이 가져다주는 마법

REST API는 HTTP라는 강력한 기반 위에서 탄생했으며, HTTP의 모든 잠재력을 최대한 활용하여 웹의 확장성과 유연성을 극대화하는 아키텍처 스타일입니다.

HTTP의 가장 강력한 특징은 무상태성(Stateless)입니다.

서버가 클라이언트의 이전 대화를 기억하지 않는다는 뜻입니다. 매번 처음 만나는 사람처럼 대화를 시작하죠. 이게 단점처럼 보이지만, 실제로는 엄청난 장점을 가져다줍니다.

서버 100대 중 아무나 요청을 처리해도 되고, 서버 한 대가 고장 나도 다른 서버가 즉시 대신할 수 있습니다. 트래픽이 갑자기 늘어나면 서버를 추가하기만 하면 되죠. 이런 확장성 덕분에 현재 인터넷이 가능한 겁니다.

 

 

REST API의 3가지 핵심 구성요소

1. 자원(Resource) - 모든 것을 URI로 표현

웹 상의 모든 자원은 고유한 주소(URI)를 가져야 합니다.

  • /users : 사용자들의 집합
  • /users/123 : 123번 사용자
  • /users/123/orders : 123번 사용자의 주문들
  • /users/123/orders/456 : 123번 사용자의 456번 주문

URI 설계 핵심 원칙:

  • 명사형(복수) 사용: /users, /orders (동사 사용 금지: /users/get, /orders/create ❌)
  • 계층적 구조: 하위 자원은 상위 자원 경로 아래에 표현
  • 직관적 이해: URI만 봐도 어떤 자원인지 바로 알 수 있어야 함

 

 

2. 행위(Verb) - HTTP 메서드로 의도 표현

자원에 대해 무엇을 할 건지 HTTP 메서드로 명확히 표현합니다.

  • GET : 조회 (멱등 - 몇 번을 해도 동일한 결과)
  • POST : 생성 (비멱등 - 할 때마다 새로운 자원 생성)
  • PUT : 전체 교체 (멱등 - 몇 번을 해도 동일한 결과)
  • PATCH : 부분 수정 (일반적으로 비멱등하나, 설계에 따라 멱등하게도 가능)
  • DELETE : 삭제 (멱등 - 몇 번을 해도 동일한 결과)

💻 HTTP 메서드 멱등성 이해하기

// 멱등한 요청들 (몇 번을 해도 결과 동일)
GET /users/123        // 항상 같은 사용자 정보 반환
PUT /users/123        // 같은 데이터로 교체하면 결과 동일
DELETE /users/123     // 이미 삭제된 것을 다시 삭제해도 결과 동일

// 비멱등한 요청들 (할 때마다 결과가 달라질 수 있음)
POST /users           // 호출할 때마다 새 사용자 생성
PATCH /users/123      // 예: {"points": 10} 추가 요청을 여러 번 보내면 매번 점수가 누적

 

 

 

3. 표현(Representation) - 데이터의 모습

클라이언트와 서버가 실제로 주고받는 데이터 형태입니다.

주로 JSON을 사용하지만, XML, HTML, 심지어 이미지나 동영상도 가능합니다. 중요한 건 클라이언트와 서버가 서로 이해할 수 있는 형태여야 한다는 점입니다.

표준화된 응답 형식 예시:

// 성공 응답
{
  "success": true,
  "data": {
    "id": 123,
    "name": "홍길동"
  }
}

// 오류 응답
{
  "success": false,
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "사용자를 찾을 수 없습니다."
  }
}

 

 

REST의 6가지 설계 원칙 - 완벽 가이드

1. 균일한 인터페이스 (Uniform Interface) - REST의 핵심

REST에서 가장 중요한 원칙으로, 4가지 세부 조건을 모두 만족해야 진정한 RESTful이라 할 수 있습니다.

① 자원의 식별 (Identification of Resources) URI만 보면 어떤 자원인지 명확히 알 수 있어야 합니다. /users/123/profile-image처럼 계층적으로 표현하면 "123번 사용자의 프로필 이미지"임을 직관적으로 이해할 수 있죠.

② 표현을 통한 자원 조작 (Manipulation through Representations) 자원 자체를 직접 건드리지 않고, 표현(JSON, XML 등)을 통해서만 조작해야 합니다. 데이터베이스를 직접 수정하는 게 아니라, API를 통해 전달받은 데이터로만 작업하는 것이죠.

③ 자기서술적 메시지 (Self-Descriptive Messages) 메시지만 봐도 완전히 이해할 수 있어야 합니다. Content-Type으로 데이터 형식을 알려주고, 상태 코드로 결과를 명시하며, 필요하다면 API 문서 링크까지 포함해야 합니다.\

 

💻 자기서술적 메시지 완벽 예시

// 요청 - 무엇을 원하는지 명확
GET /users/123 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer abc123

// 응답 - 모든 정보가 포함됨
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: max-age=3600
Link: <https://api.example.com/docs/users>; rel="profile"

{
  "id": 123,
  "name": "홍길동",
  "email": "hong@example.com",
  "created_at": "2024-01-15T10:30:00Z"
}

 

④ 하이퍼미디어 (HATEOAS - Hypermedia as the Engine of Application State) 응답에 다음에 할 수 있는 행동들의 링크를 포함해야 합니다. 웹페이지에 다른 페이지로 갈 수 있는 링크가 있는 것처럼, API 응답에도 다음 단계 링크가 있어야 한다는 뜻입니다.

HATEOAS는 REST의 궁극적인 목표이자, API 탐색 가능성을 높이는 핵심 원칙입니다. 이 원칙을 만족해야 진정한 RESTful API라고 부를 수 있습니다.

 

💻 HATEOAS 실무 구현 예시

{
  "id": 123,
  "name": "홍길동",
  "email": "hong@example.com",
  "status": "active",
  "_links": {
    "self": {
      "href": "/users/123"
    },
    "edit": {
      "href": "/users/123",
      "method": "PUT"
    },
    "orders": {
      "href": "/users/123/orders"
    },
    "deactivate": {
      "href": "/users/123/deactivate",
      "method": "POST"
    }
  }
}

 

 

 

2. 클라이언트-서버 분리 (Client-Server)

클라이언트와 서버가 서로 독립적으로 진화할 수 있어야 합니다.

쇼핑몰 앱(클라이언트)이 업데이트되어도 서버에는 영향이 없고, 서버의 내부 로직이 바뀌어도 앱은 계속 동작해야 합니다. 이렇게 역할을 분리하면 개발 팀도 독립적으로 일할 수 있고, 시스템 유지보수도 훨씬 쉬워집니다.

 

 

3. 무상태성 (Stateless)

서버는 클라이언트의 상태를 기억하지 않습니다.

매 요청마다 "누가, 무엇을, 어떻게" 하고 싶은지 완전한 정보를 제공해야 합니다. 마치 콜센터에 전화할 때마다 본인 확인을 다시 하는 것과 같죠.

 

💻 무상태성 구현 방법

// 상태 의존적 방식 (잘못된 예)
POST /login
POST /orders    // 누가 주문하는지 서버가 기억해야 함

// 무상태 방식 (올바른 예)
POST /orders
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "user_id": 123,
  "items": [{"product_id": 456, "quantity": 2}]
}

 

 

4. 캐시 가능성 (Cacheable)

자주 요청되는 데이터는 캐시해서 성능을 향상시킬 수 있어야 합니다.

HTTP 표준의 캐시 헤더(Cache-Control, ETag, Last-Modified 등)를 활용해 클라이언트나 중간 서버가 데이터를 임시 저장할 수 있게 합니다. 상품 목록이나 공지사항처럼 자주 바뀌지 않는 데이터를 매번 서버에서 가져올 필요가 없어지죠.

 

 

5. 계층화된 시스템 (Layered System)

클라이언트와 서버 사이에 여러 중간 계층이 있어도 투명하게 동작해야 합니다.

실제 서비스에서는 로드밸런서, 방화벽, 캐시 서버, API 게이트웨이 등이 여러 층으로 구성됩니다. 하지만 클라이언트는 이런 복잡한 구조를 몰라도 되고, 마치 서버와 직접 통신하는 것처럼 느껴야 합니다.

 

 

6. 코드 온 디맨드 (Code-On-Demand) - 선택적 조건

서버가 클라이언트에게 실행 가능한 코드를 전송할 수 있습니다.

JavaScript 코드나 플러그인을 응답에 포함해서 클라이언트의 기능을 확장할 수 있지만, 이는 필수가 아닌 선택적 조건입니다. 보안 문제나 복잡성 때문에 실제로는 잘 사용하지 않습니다.

 

 

실무에서 꼭 알아야 할 추가 개념들

멱등성 - 반복해도 안전한 요청 구분하기

멱등성은 동일한 요청을 여러 번 해도 서버 상태가 같게 유지되는 성질입니다.

네트워크 오류로 요청이 중복될 수 있기 때문에 매우 중요한 개념입니다. GET, PUT, DELETE는 멱등하지만, POST와 PATCH는 비멱등합니다.

실무 해결책:

  • POST 요청에 고유 ID(idempotency key)를 포함
  • 중복 방지 로직 구현
  • 클라이언트 측에서 재시도 정책 적용

API 버전 관리 - 진화하는 시스템 설계

API는 시간이 지나면서 변경될 수밖에 없습니다.

기존 클라이언트의 호환성을 유지하면서 새 기능을 추가하려면 버전 관리가 필수입니다.

버전 관리 방법:

  • URL 패스: /v1/users, /v2/users
  • 헤더: Accept: application/vnd.api+json;version=2
  • 쿼리 파라미터: /users?version=2

보안 필수 요소 - 인증과 인가

인증(Authentication)은 "누구인지 확인"하는 과정이고, 인가(Authorization)는 "무엇을 할 수 있는지 확인"하는 과정입니다.

주요 보안 고려사항:

  • JWT 토큰 기반 인증
  • CORS(Cross-Origin Resource Sharing) 설정
  • Rate Limiting으로 남용 방지
  • HTTPS 필수 사용

CORS - 웹 브라우저 보안 제약

클라이언트가 다른 도메인에서 API를 호출할 때 발생하는 보안 제약입니다. 웹 브라우저가 악의적인 사이트에서 다른 사이트의 API를 무단 호출하는 것을 방지하기 위한 보안 메커니즘입니다.

CORS는 서버가 특정 도메인(origin)의 요청을 허용할지 결정하는 메커니즘이며, 서버에서 Access-Control-Allow-Origin 등의 헤더를 설정하여 허용할 도메인을 명시해야 합니다.

Soft Delete - 현실적인 삭제 처리

실무에서는 데이터를 완전히 삭제하는 경우가 드뭅니다.

법적 요구사항이나 데이터 복구 필요성 때문에 deleted_at 필드만 설정하는 Soft Delete를 주로 사용합니다. DELETE 메서드를 쓰지만 실제로는 UPDATE 작업을 수행하는 것이죠.

API Gateway - 마이크로서비스 통합 관리

마이크로서비스 환경에서 여러 API를 통합하고 관리하는 중요한 계층입니다. 인증, 로깅, 캐싱, 라우팅 등의 공통 기능을 중앙화하여 관리할 수 있습니다.

'spring' 카테고리의 다른 글

DTO 객체 생성, 상황별 최적 전략은?  (1) 2025.06.18
[Spring] @Transactional  (2) 2025.06.15

+ Recent posts