DBMS(Database Management System)

📌 데이터베이스를 생성, 관리, 유지보수, 운영하기 위한 소프트웨어입니다. 사용자는 DBMS를 통해 데이터를 효율적으로 저장, 검색, 수정, 삭제하며, 데이터의 일관성과 무결성을 유지할 수 있습니다.

  • 데이터베이스 구조를 정의할 수 있는 기능을 제공한다.

 

DBMS의 역할

  1. 데이터 관리
    • 데이터를 물리적으로 저장하고, 관리하는 역할을 수행한다.
    • 데이터를 저장하기 위한 최적화된 구조와 파일 시스템을 관리합니다.
    • 사용자가 데이터를 다룰 수 있도록 쿼리 언어(SQL)을 제공한다.
  2. 데이터 보안
    • 사용자 권한 관리, 암호화, 감사 로그 등을 통해 데이터를 보호한다.
  3. 트랜잭션 관리
    • DBMS는 여러 사용자가 동시에 데이터에 접근할 때, 데이터의 일관성을 유지하기 위한 트랜잭션 관리 기능을 제공한다.
    • ACID 속성 보장
      • Atomicity: 트랜잭션의 모든 작업이 성공적으로 완료되거나, 실패 시 모든 작업이 롤백
      • Consistency: 트랜잭션이 데이터베이스를 일관된 상태로 유지
      • Isolation: 동시에 실행되는 트랜잭션 간의 영향을 최소화
      • Durability: 트랜잭션이 완료된 후 데이터의 변경 사항은 영구적으로 저장
  4. 백업 및 복구
    • DBMS는 데이터 손실에 대비해 백업 및 복구 기능을 제공한다.
    • 정기적인 백업을 통해 데이터를 보호하며, 장애 발생 시 데이터 복구가 가능하다.
  5. 동시성 제어
    • 다수의 사용자가 동시에 데이터베이스에 접근하더라도 데이터 일관성이 유지되도록 동시성 제어를 제공한다. 이를 통해 충돌이나 데이터 불일치를 방지할 수 있다.

 

 

DBMS의 주요 기능

데이터 정의 (DDL) 데이터베이스 구조(스키마) 정의. 테이블 생성, 수정, 삭제 등을 처리.
데이터 조작 (DML) 데이터를 추가, 검색, 수정, 삭제.
데이터 제어 (DCL) 데이터 접근 권한 및 제어.
트랜잭션 관리 (TCL) 데이터베이스의 일관성과 무결성을 보장하기 위해 트랜잭션을 관리.

 

DBMS의 구조

a. 사용자:

  • 데이터베이스와 상호작용하는 최종 사용자.
  • 애플리케이션, 관리자, 또는 일반 사용자가 포함.

b. DBMS:

  • 데이터베이스를 관리하고 사용자 요청을 처리하는 소프트웨어.

c. 데이터베이스:

  • 데이터가 실제로 저장되는 영역.

 

DBMS의 종류

a. 관계형 데이터베이스 관리 시스템 (RDBMS)

  • 가장 많이 사용하는 데이터베이스
  • 데이터를 테이블 형태로 저장.
  • 테이블 간 관계를 키(Foreign Key)로 정의.
  • SQL(Structured Query Language)을 사용.
  • 예시: MySQL, PostgreSQL, Oracle Database, Microsoft SQL Server.

b. 비관계형 데이터베이스 관리 시스템 (NoSQL DBMS)

  • 테이블이 아닌 key-value, document, graph 등의 다양한 형태로 데이터를 저장하고 관리한다.
  • 스키마가 고정되지 않고, 대규모 데이터 처리와 높은 확장성을 제공한다.
  • 예시: MongoDB, Cassandra, Redis, Neo4j.

c. 분산 데이터베이스 관리 시스템

  • 데이터가 여러 노드에 분산되어 저장.
  • 대규모 데이터를 처리하기 위한 시스템.
  • 예시: Amazon DynamoDB, Google Bigtable.

d. 클라우드 기반 DBMS

  • 클라우드 환경에서 데이터베이스를 관리.
  • 유연성과 확장성이 높음.
  • 예시: AWS RDS, Google Cloud SQL, Azure SQL Database.

 

DBMS의 구성 요소

구성 요소 설명
데이터베이스 엔진 데이터 저장, 검색, 수정, 삭제 등의 핵심 작업 수행.
스키마 데이터베이스 구조 정의.
질의 처리기 SQL 질의를 처리하고 결과 반환.
트랜잭션 관리자 데이터 무결성과 일관성을 유지하며 트랜잭션 관리.
보안 관리자 데이터 접근 제어 및 권한 관리.
백업 및 복구 관리자 데이터 손실 방지 및 복구 작업 수행.

 

DBMS와 파일 시스템의 차이점

  DBMS 파일 시스템
데이터 중복 제거 데이터 중복을 최소화 (정규화). 데이터 중복 가능성이 높음.
데이터 무결성 무결성 제약 조건으로 데이터의 일관성 유지. 무결성 관리가 어렵고 사용자에 의존.
보안성 접근 제어와 인증을 통한 강력한 보안 제공. 파일 수준의 제한적 보안.
트랜잭션 관리 ACID 속성을 통해 트랜잭션 처리. 트랜잭션 관리 기능이 없음.
검색 속도 인덱싱 등으로 고속 데이터 검색 가능. 검색 속도가 느림.

 

 

주요 DBMS 예시

DBMS 설명
MySQL 오픈 소스 관계형 DBMS, 높은 성능과 안정성 제공.
PostgreSQL 고급 기능과 표준 준수를 중시하는 오픈 소스 RDBMS.
Oracle 대기업에서 널리 사용되는 상용 관계형 DBMS.
MongoDB 문서 기반의 NoSQL DBMS.
Redis 키-값 기반의 인메모리 데이터베이스, 빠른 속도를 제공.

 

 

 

트랜잭션(Transaction)

📌 데이터베이스 관리에서 논리적으로 하나의 작업 단위를 의미합니다.

  • 트랜잭션은 여러 작업(쿼리)이 하나의 단위로 묶여 모두 성공하거나, 모두 실패하는 특성을 가집니다.
더보기

 

트랜잭션의 예

a. 은행 계좌 이체 예제

  • A 계좌에서 100원을 출금 → B 계좌에 100원을 입금.
  • 두 작업은 하나의 트랜잭션으로 묶여야 함.

1. 트랜잭션 성공

  1. A 계좌에서 100원 차감.
  2. B 계좌에 100원 추가.
  3. Commit: 두 작업 모두 성공 → 데이터베이스에 반영.

2. 트랜잭션 실패

  1. A 계좌에서 100원 차감.
  2. B 계좌에 입금 실패.
  3. Rollback: 두 작업 모두 취소 → A 계좌의 100원 복원.

트랜잭션의 특징: ACID

트랜잭션은 데이터의 안정성과 무결성을 보장하기 위해 ACID 특성을 만족해야 합니다.

Atomicity (원자성) 트랜잭션의 모든 작업이 모두 성공하거나 모두 실패해야 함.
Consistency (일관성) 트랜잭션 전후 데이터베이스의 상태가 일관성을 유지해야 함.
Isolation (고립성) 동시에 실행되는 트랜잭션이 서로 간섭하지 않도록 독립적으로 실행됨을 보장.
Durability (지속성) 트랜잭션이 성공적으로 완료되면, 그 결과는 영구적으로 반영되어야 함.

 

트랜잭션의 생명주기

  1. Begin:
    • 트랜잭션 시작.
  2. Execute:
    • 트랜잭션 내 작업(쿼리) 실행.
  3. Commit:
    • 트랜잭션 성공 → 작업 내용을 데이터베이스에 반영.
  4. Rollback:
    • 트랜잭션 실패 → 작업 내용을 데이터베이스에서 취소.

 

트랜잭션 격리 수준 (Isolation Level)

트랜잭션 간의 간섭을 최소화하기 위해 격리 수준을 설정합니다.
Spring에서는 @Transactional의 isolation 속성을 사용해 설정합니다.

격리 수준 설명
READ_UNCOMMITTED 다른 트랜잭션의 미완료 데이터를 읽을 수 있음. (Dirty Read 허용)
READ_COMMITTED 다른 트랜잭션이 Commit한 데이터만 읽을 수 있음. (기본값)
REPEATABLE_READ 트랜잭션 내에서 같은 데이터를 여러 번 읽을 때 항상 동일한 값 반환.
SERIALIZABLE 가장 높은 격리 수준. 트랜잭션이 순차적으로 실행되며 교차 작업 불가.

 

 

Spring에서 트랜잭션 관리

더보기

Spring은 트랜잭션 관리를 위한 간단하고 강력한 기능을 제공합니다.
주로 @Transactional 어노테이션을 사용합니다.

 

a. 기본 사용법

@Service
public class BankService {

    private final AccountRepository accountRepository;

    public BankService(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }

    @Transactional
    public void transferMoney(Long fromAccountId, Long toAccountId, int amount) {
        Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
        Account toAccount = accountRepository.findById(toAccountId).orElseThrow();

        fromAccount.withdraw(amount);  // 출금
        toAccount.deposit(amount);    // 입금

        accountRepository.save(fromAccount);
        accountRepository.save(toAccount);
    }
}

@Transactional:

  • 메서드 내의 모든 작업이 하나의 트랜잭션으로 처리.
  • 하나라도 실패하면 Rollback.

 

Rollback 처리

Spring에서 특정 예외 발생 시 트랜잭션을 Rollback합니다.

예외 처리 예제

@Transactional(rollbackFor = CustomException.class)
public void processTransaction() throws CustomException {
    // 작업 실행
    if (errorCondition) {
        throw new CustomException("Error occurred");
    }
    // 다른 작업 실행
}
  • rollbackFor: 특정 예외 발생 시 Rollback 설정.
  • 기본적으로 RuntimeException이나 Error가 발생하면 Rollback.

 

관계형 데이터베이스 ( RDB )

📌 데이터를 테이블(Table) 형태로 저장하고, 관계(Relationship)를 통해 데이터를 연결하여 관리하는 데이터베이스입니다. 관계형 모델을 기반으로 설계되었으며, 데이터를 행(Row)과 열(Column)의 구조로 저장합니다.

  • RDB는 주로 **SQL(Structured Query Language)**을 사용하여 데이터를 정의, 관리, 검색합니다.

 

RDB 설계 시 주요 개념

a. 정규화(Normalization)

  • 데이터 중복을 최소화하고 데이터 구조를 최적화.

b. 비정규화(Denormalization)

  • 성능 향상을 위해 일부 데이터 중복 허용.

c. 키(Key)

  • Primary Key: 테이블의 각 행을 고유하게 식별.
  • Foreign Key: 다른 테이블의 Primary Key를 참조.

d. 인덱스(Index)

  • 데이터 검색 속도를 향상시키는 구조.

e. 뷰(View)

  • 실제 데이터 없이 쿼리 결과를 저장하는 가상 테이블.

 

RDBMS와의 관계

**RDB(Relational Database)**는 관계형 데이터 모델을 기반으로 설계된 데이터베이스이며, 이를 관리하는 소프트웨어를 **RDBMS(Relational Database Management System)**라고 합니다.

RDB 관계형 데이터 모델에 따라 설계된 데이터베이스.
RDBMS RDB를 생성, 관리, 유지보수하는 소프트웨어 시스템.
SQL RDB와 RDBMS에서 데이터를 처리하기 위해 사용하는 언어.

 

RDB의 데이터 모델

a. 1:1 관계

  • 한 테이블의 행이 다른 테이블의 행과 1:1로 매핑.
  • 예: 사람(Person) ↔ 여권(Passport)

b. 1:N 관계

  • 한 테이블의 행이 다른 테이블의 여러 행과 매핑.
  • 예: 사용자(User) ↔ 주문(Order)

c. N:M 관계

  • 두 테이블의 여러 행이 서로 다수의 행과 매핑.
  • 예: 학생(Student) ↔ 수업(Class)

 

+ 해결 방법: 중간 테이블(Join Table) 사용.

  • 학생(Student) ↔ 수강 기록(Student_Class) ↔ 수업(Class)

 

 

관계형 데이터베이스 특징

 

테이블 (Table)

 

  • RDBMS에서 데이터는 테이블이라는 구조에 저장되며 행(row)과 열(column)로 구성된다.
  • 열(column)은 데이터의 속성(유일한 이름)을 나타내고 타입(데이터 유형)을 가진다.
  • 행(row)은 관계된 데이터의 묶음을 의미하고 tuple 또는 record라고 한다.
  • 데이터 무결성
    • 테이블은 특정 규칙과 제약 조건(기본 키, 외래 키, 유니크 등)을 통해 데이터를 저장함으로써 데이터의 무결성(정확성, 일관성, 유효성)을 유지한다.

 

관계 (Relationships)

  • 테이블 간의 관계는 외래 키(Foreign Key)를 통해 설정된다.
  • RDBMS는 다양한 유형의 관계를 지원한다.
    • 1:1 관계: 한 테이블의 한 행이 다른 테이블의 한 행과만 연결된다.
    • 1:다 관계: 한 테이블의 한 행이 다른 테이블의 여러 행과 연결된다.(위 예시와 같음)
    • 다:다 관계: 두 테이블의 여러 행이 서로 연결될 수 있다.

 

SQL (Structured Query Language)

  • RDBMS에서 데이터를 정의하고, 관리하기 위한 표준 언어이다.
  • 데이터를 생성(Create), 읽기(Read), 갱신(Update), 삭제(Delete)하는 작업을 수행한다.

 

키 (Keys)

  • 기본 키(Primary Key)
    • 테이블 내에서 각 행을 고유하게 식별하는 열 또는 열의 조합이다.
    • 기본 키는 중복되지 않으며, NULL 값을 가질 수 없다.
  • 외래 키(Foreign Key)
    • 한 테이블의 열이 다른 테이블의 기본 키를 참조하여 두 테이블 간의 관계를 설정하는 데 사용된다.
    • 테이블 간의 데이터 무결성을 유지할 수 있다.
  • 유일 키(Unique Key)
    • 기본 키와 유사하지만, 하나의 테이블에서 여러 개가 존재할 수 있다.
    • 중복된 값을 허용하지 않지만, NULL 값은 허용할 수 있다.

 

트랜잭션(Transaction)

  • RDBMS는 트랜잭션이라는 단위를 통해 데이터베이스 작업을 처리하며, 이를 통해 데이터의 일관성과 무결성을 유지한다.
  • 트랜잭션은 원자성(Atomicity), 일관성(Consistency), 고립성(Isolation), 지속성(Durability)이라는 ACID 속성을 따른다.
    • Atomicity: 트랜잭션의 모든 작업이 성공적으로 완료되거나, 실패 시 모든 작업이 롤백
    • Consistency: 트랜잭션이 데이터베이스를 일관된 상태로 유지
    • Isolation: 동시에 실행되는 트랜잭션 간의 영향을 최소화
    • Durability: 트랜잭션이 완료된 후 데이터의 변경 사항은 영구적으로 저장

 

정규화 (Normalization)

  • 데이터의 중복을 줄이고, 일관성과 무결성을 유지하기 위해 데이터를 구조화하는 프로세스이다.
  • 여러가지 정규화 단계가 있으며, 각 단계는 데이터 중복을 줄이고 이상 현상을 방지하는 데 목적이 있다.

 

데이터 무결성 (Data Integrity)

  • 엔터티 무결성
    • 각 테이블의 기본 키(PK)가 중복되지 않고 NULL 값이 아닌 상태를 유지한다.
  • 참조 무결성
    • 외래 키(FK)를 통해 참조되는 데이터가 유효성을 유지하도록 보장한다.
  • 도메인 무결성
    • 각 열이 정의된 데이터 타입과 제약 조건에 따라 유효한 값을 유지하도록 한다.

 

인덱스 (Index)

  • 특정 열의 검색 성능을 향상시키기 위해 사용된다.
  • 인덱스는 테이블의 데이터를 정렬하고, 효율적으로 접근할 수 있도록 지원한다.
  • 인덱스가 많아지면 삽입 및 수정 작업의 성능에 영향을 미칠 수 있다.

 

 

RDBMS

📌 관계형 데이터베이스 RDB(Relational DataBase)를 관리할 수 있는 소프트웨어로 데이터를 테이블 형식으로 관리한다. RDBMS는 데이터 간의 관계를 정의하고, 이러한 관계를 바탕으로 복잡한 Query를 실행할 수 있는 기능을 제공한다.

  • RDBMS는 관계형 모델을 기반으로 데이터의 일관성, 무결성, 동시성을 보장합니다.

 

RDBMS의 특징

  1. 테이블 기반:
    • 데이터를 행(Row)과 열(Column)로 구성된 테이블에 저장.
    • 테이블은 데이터베이스 내에서 독립적으로 관리.
  2. SQL 사용:
    • 데이터를 관리하기 위해 SQL(Structured Query Language) 사용.
  3. 키(Key)와 관계(Relationship):
    • 기본 키(Primary Key)와 외래 키(Foreign Key)를 통해 데이터 무결성 및 테이블 간 관계를 관리.
  4. 데이터 무결성:
    • 데이터가 정확하고 일관되도록 보장.
  5. 트랜잭션 지원:
    • 트랜잭션을 통해 작업의 원자성, 일관성, 고립성, 지속성을 보장 (ACID 특성).

 

 

RDBMS의 구성 요소

a. 테이블 (Table)

  • 데이터 저장 구조.
  • 행(Row)과 열(Column)로 구성.
  • 각 열은 속성(Attribute), 각 행은 레코드(Record).

b. 열(Column)

  • 테이블의 필드(Field).
  • 데이터의 유형과 속성을 정의.

c. 행(Row)

  • 각 행은 데이터의 한 개체(Entity)를 나타냄.

d. 기본 키 (Primary Key)

  • 테이블의 각 행을 고유하게 식별하는 열.

e. 외래 키 (Foreign Key)

  • 다른 테이블의 기본 키를 참조하여 테이블 간 관계를 정의.

f. 인덱스 (Index)

  • 데이터 검색 속도를 높이기 위해 사용하는 구조.

 

주요 RDBMS 예시

RDBMS 특징
MySQL 오픈 소스 RDBMS, 높은 성능과 안정성 제공.
PostgreSQL 오픈 소스 RDBMS, 고급 기능과 확장성 제공.
Oracle Database 상용 RDBMS, 대규모 데이터 처리와 강력한 보안 제공.
Microsoft SQL Server Microsoft에서 제공하는 상용 RDBMS, 윈도우 환경에 최적화.
SQLite 경량형 RDBMS, 모바일 및 임베디드 환경에 적합.

 

 

RDBMS와 NoSQL 비교

  RDBMS NoSQL
데이터 구조 테이블 (행과 열) 문서, 키-값, 컬럼, 그래프 등 다양한 형태.
스키마 고정 스키마 필요 스키마 유연.
데이터 관계 기본 키와 외래 키로 관계 정의 관계를 덜 중요시하거나 애플리케이션에서 관리.
확장성 수직 확장에 적합 수평 확장에 적합.
사용 사례 금융, ERP, CRM 등 일관성과 무결성이 중요한 애플리케이션 소셜 네트워크, IoT, 대규모 데이터 처리.

 

 

 

SQL(Structured Query Language)

📌 관계형 데이터베이스 관리 시스템(RDBMS)에서 데이터를 정의, 조작, 제어, 조회하기 위해 사용되는 표준 프로그래밍 언어이다.

 

SQL 특징

  • 관계형 데이터베이스와의 상호작용을 표준화하고 효율적으로 수행할 수 있게 해준다.
    • 데이터베이스에서 원하는 정보를 추출하고 분석할 수 있게 해준다.
  • 대부분의 RDBMS(MySQL, PostgreSQL, Oracle 등)가 SQL을 지원한다.
  • 주의! 표준 SQL은 존재하지만, 제품마다 조금씩의 차이(함수명)가 존재한다.
  • SQL 명령문은 대소문자를 구분하지 않고, 대문자로 사용하면 가독성이 향상되어 오타를 방지한다.

 

SQL 종류

DDL(Data Definition Language)

  • 데이터베이스 구조를 정의하는 데 사용된다.
  • CREATE
    • 새로운 데이터베이스 및 테이블을 생성한다.
  • ALTER
    • 기존 데이터베이스 및 테이블 구조를 수정한다.
  • DROP
    • 데이터베이스 및 테이블을 삭제한다.

DML(Data Manipulation Language)

  • 데이터베이스의 데이터를 조작하는 데 사용된다.
  • INSERT
    • 데이터를 테이블에 삽입한다.
  • UPDATE
    • 테이블의 기존 데이터를 수정한다.
  • DELETE
    • 테이블의 데이터를 삭제한다.

DQL(Data Query Language)

  • 데이터베이스에서 데이터를 검색하는 데 사용된다.
  • SELECT
    • 데이터를 조회한다. 특정 조건을 추가할 수 있다.

DCL(Data Control Language)

  • 데이터베이스의 권한을 관리하는 데 사용된다.
  • GRANT
    • 사용자에게 권한을 부여한다.
  • REVOKE
    • 사용자의 권한을 회수한다.

TCL(Transaction Control Language)

  • 여러 DML 작업을 하나의 논리적 단위로 묶어 트랜잭션으로 처리하는 데 사용된다.
  • COMMIT
    • 트랜잭션이 성공한 것을 데이터베이스에 알리고 모든 변경 사항을 영구적으로 저장한다.
  • ROLLBACK
    • 트랜잭션 중 발생한 모든 변경 사항을 취소하고, 데이터베이스를 트랜잭션 시작 시점의 상태로 되돌린다.

 

SQL의 주요 개념

a. 키(Key)

  • Primary Key (기본 키):
    • 테이블에서 각 행(row)을 고유하게 식별하는 하나 이상의 열(column)에 설정되는 제약조건
      • 중복된 데이터가 테이블에 삽입되는 것을 방지한다.
  • NOT NULL과 UNIQUE 제약 조건의 특징을 모두 가진다.
    • INDEX로 설정되어 테이블의 데이터를 쉽고 빠르게 찾도록 도와주는 역할을 한다.
      • 추가적인 쓰기 작업과 저장 공간을 활용하여 검색 속도를 향상시키기 위한 자료구조
CREATE TABLE students (
    id BIGINT PRIMARY KEY,               -- 학생 고유 번호 (자동 증가)
    name VARCHAR(50) NOT NULL,           -- 이름
    email VARCHAR(100) NOT NULL UNIQUE,  -- 이메일
    PRIMARY KEY (id)                     -- PRIMARY KEY 설정
);
    •  
  • Foreign Key (외래 키):
    • 한 테이블의 열이 다른 테이블의 PRIMARY KEY(또는 UNIQUE 제약 조건이 적용된 열)를 참조하도록 설정한다.
      • 참조된 테이블의 데이터가 변경되거나 삭제될 때 참조 무결성을 강제할 수 있다.
        • 외래 키 값은 반드시 참조하는 테이블의 기본 키로 존재해야 한다.
        • 부모 테이블의 데이터가 변경되거나 삭제될 때 자식 테이블에 있는 데이터도 자동으로 업데이트되거나 삭제되도록 보장한다.(CASCADE)
    • 다른 테이블의 Primary Key를 참조.
    • 테이블 간 관계를 정의.
CREATE TABLE 테이블이름
(
    필드이름 필드타입,
    ...
		FOREIGN KEY(필드이름)
    REFERENCES 테이블이름(필드이름)
);

 

  • CASCADE
    • 참조 무결성을 유지하기 위한 동작을 정의하는 규칙
    • 외래 키(Foreign Key) 제약 조건과 관련된 변경 사항이 발생할 때 참조하는 레코드에 대한 동작을 자동으로 처리하는 기능
      • FOREIGN KEY 로 연관된 데이터를 삭제,변경할 수 있다.
CREATE TABLE 테이블이름
(
    필드이름 필드타입,
    ...
		FOREIGN KEY(필드이름)
    REFERENCES 테이블이름(필드이름) ON DELETE CASCADE 
														 //ON UPDATE CASCADE
);


1. `ON DELETE CASCADE`
    - 부모 테이블의 행이 삭제되면, 참조하는 자식 테이블의 관련 행들도 자동으로 삭제한다.
    
2. `ON UPDATE CASCADE`
    - 부모 테이블의 기본 키가 업데이트되면, 참조하는 자식 테이블의 외래 키 값도 자동으로 수정된다.
  • DEFAULT
    • 해당 필드의 기본 값을 설정한다.
    • 필드 값이 전달되지 않으면, 자동으로 기본 값을 저장한다.
CREATE TABLE 테이블이름
(
    필드이름 필드타입 DEFAULT 값
);
  • Unique Key:
    • 중복되지 않는 고유한 값을 유지.

 

b. 제약 조건(Constraints)

  • 데이터 무결성을 유지하기 위한 규칙.
    • AUTO_INCREMENT : 고유번호 자동생성(컬럼의 값이 중복되지 않게 1씩 자동으로 증가)
    • NOT NULL: NULL 값 불가.
    • UNIQUE: 중복 값 불가.
    • CHECK: 특정 조건 만족.
    • DEFAULT: 기본값 설정.
    • FOREIGN KEY: 테이블 간 관계 설정.
-- users 테이블 생성
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,       -- 고유번호 자동생성 (Primary Key 포함)
    name VARCHAR(100) NOT NULL,              -- 이름은 NULL 값 불가
    email VARCHAR(100) UNIQUE NOT NULL,      -- 이메일은 중복 불가 및 NULL 값 불가
    age INT CHECK (age >= 18),               -- 나이는 18세 이상이어야 함
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 기본값으로 현재 시간 설정
);

-- orders 테이블 생성
CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,       -- 고유번호 자동생성 (Primary Key 포함)
    user_id INT NOT NULL,                    -- 사용자 ID (외래 키)
    amount DECIMAL(10, 2) CHECK (amount > 0),-- 금액은 0보다 커야 함
    order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 주문 날짜 기본값
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -- users 테이블과 관계 설정, 사용자 삭제 시 주문도 삭제
);
  • 데이터 무결성
    • 데이터의 정확성, 일관성, 완전성을 유지하는 것
      1. 정확성: 데이터가 올바르고 오류 없이 저장되는 것을 의미한다.
      ex) 숫자로 저장되어야 하는 값이 문자로 저장되지 않도록 하는 것.
      1. 일관성: 데이터가 서로 모순되지 않고 조화를 이루는 상태를 유지하는 것을 의미한다.
      ex) 외래 키 제약 조건을 통해 두 테이블 간의 관계가 일치하는 것을 보장하는 것.
      1. 완전성: 필요한 모든 데이터가 빠짐없이 저장되고 관리되는 것을 의미한다.
      ex) 필수 입력 항목이 비어 있지 않도록 하는 것.
    • 즉, 데이터가 입력, 저장, 전송, 처리되는 동안 변경되거나 손상되지 않도록 보장하는 개념

 

c. JOIN

  • 두개 이상의 테이블을 연결하여 데이터를 검색하는 방법
  • 테이블을 분리하여 데이터 중복을 최소화하고 데이터의 일관성을 유지하기 위해 사용된다.
  • JOIN 종류
  • 여러 테이블의 데이터를 결합하여 조회.

  1. INNER JOIN
    • 두 테이블에서 공통된 값을 가지고 있는 행만 반환한다.
      • ON 절의 조건이 일치하는 결과
    • MySQL에서는 JOIN, INNER JOIN, CROSS JOIN이 모두 같은 의미이다.
    • 교집합
  2. LEFT JOIN
    • 왼쪽 테이블의 모든 행과 오른쪽 테이블의 일치하는 행을 반환한다.
      • ON 절의 조건 중 첫번째 테이블인 왼쪽(기준)의 데이터를 모두 가져온다.
    • 오른쪽 테이블에 일치하는 데이터가 없으면 NULL로 반환한다.
    • JOIN을 여러번 사용하는 경우 LEFT JOIN으로 시작했다면 이후 JOIN도 LEFT JOIN으로 해야한다.
    • 부분 집합(왼쪽 테이블)
  3. RIGHT JOIN
    • 오른쪽 테이블의 모든 행과 왼쪽 테이블의 일치하는 행을 반환한다.
      • ON 절의 조건 중 두번째 테이블인 오른쪽(기준)의 데이터를 모두 가져온다.
    • 왼쪽 테이블에 일치하는 데이터가 없으면 NULL로 반환한다.
    • 부분 집합(오른쪽 테이블)
  4. OUTER JOIN
    • 두 테이블에서 공통된 값을 가지지 않는 행도 포함해서 반환한다.
    • 합집합
    • LEFT OUTTER JOIN, RIGHT OUTTER JOIN, FULL OUTTER JOIN(잘 사용하지 않음) 이 있다.
    • 대부분의 DB는 FULL OUTTER JOIN을 지원하지 않고 UNION을 사용하도록 한다.
      • UNION을 사용하면 자동으로 중복을 제거(DISTICT)해준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 

 

📌 

  •  

 

 


 

 

 🐳 

  •  

 

 

 

 🐳 

  •  

 

 

 

 🐳 

  •  

 

 


 

소제목 

🧩 부모 타입 변수 = 자식 타입 객체; 는 자동으로 부모 타입으로 변환이 일어납니다.

 


 

소제목 

🎵 클래스가 설계도라면 추상 클래스는 미완성된 설계도입니다.

 

Database

📌 데이터를 체계적으로 저장하고 관리하기 위한 시스템으로, 데이터를 저장, 검색, 수정, 삭제할 수 있는 기능을 제공하며, 애플리케이션과 사용자 간의 효율적인 데이터 관리를 가능하게 합니다.

 

데이터베이스의 특징

  1. 조직적 데이터 저장:
    • 데이터를 테이블, 행, 열 등 구조화된 형태로 저장.
  2. 데이터 무결성:
    • 데이터의 정확성과 일관성을 유지.
  3. 동시성:
    • 여러 사용자가 데이터를 동시에 사용할 수 있도록 지원.
  4. 보안성:
    • 인증 및 권한 부여를 통해 데이터 접근을 제어.
  5. 백업 및 복구:
    • 데이터 손실 방지를 위해 백업 및 복구 기능 제공.

 

데이터베이스의 주요 개념

a. 스키마 (Schema)

  • 데이터베이스의 구조를 정의한 설계도.
  • 테이블, 뷰, 인덱스, 트리거 등을 포함.

b. 트랜잭션 (Transaction)

  • 데이터베이스의 논리적 작업 단위.
  • ACID 특성을 보장:
    • Atomicity: 작업은 모두 성공하거나 모두 실패.
    • Consistency: 트랜잭션 후 데이터 일관성 유지.
    • Isolation: 여러 트랜잭션이 독립적으로 실행.
    • Durability: 작업이 완료되면 영구적으로 저장.

c. 인덱스 (Index)

  • 데이터 검색 속도를 향상시키기 위한 구조.
  • 예: 책의 목차와 같은 역할.

d. 관계 (Relation)

  • 데이터베이스 테이블 간의 연결.
  • 예: 외래 키(Foreign Key)를 통해 사용자와 주문 테이블 연결.

 

데이터베이스 설계

a. 정규화 (Normalization)

  • 중복 데이터를 제거하고 데이터 구조를 최적화.
  • 데이터를 여러 테이블로 나누고 관계를 정의.

b. 비정규화 (Denormalization)

  • 읽기 성능을 높이기 위해 정규화를 일부 해제.
  • 데이터 중복을 허용.

 

Spring과 데이터베이스 통합

Spring Framework는 데이터베이스와의 상호작용을 쉽게 하기 위해 다양한 도구를 제공합니다.

a. JDBC (Java Database Connectivity)

  • Java에서 데이터베이스와 연결하기 위한 표준 API.

b. JPA (Java Persistence API)

  • Java 객체와 관계형 데이터베이스 간의 매핑을 위한 API.
  • Hibernate가 JPA의 대표적인 구현체.

 

 

데이터 모델링 (Data Modeling)

📌 현실 세계의 데이터를 데이터베이스에 저장하고 관리하기 위해 구조화하는 과정입니다.

  • 데이터 간의 관계와 속성을 정의하여 체계적으로 데이터를 표현하고 설계합니다.
  • 데이터 모델링은 데이터베이스 설계의 핵심 단계이며, 애플리케이션의 데이터 흐름과 사용 방식을 반영합니다.

 

데이터 모델링의 목적

  1. 데이터 구조 정의:
    • 데이터를 저장할 테이블과 속성을 정의.
  2. 데이터 관계 설정:
    • 테이블 간의 관계(1:1, 1:N, N:N)를 정의.
  3. 데이터 무결성 보장:
    • 중복 데이터를 최소화하고 데이터의 일관성을 유지.
  4. 성능 최적화:
    • 효율적인 데이터 검색과 처리를 지원.
  5. 시스템 요구 사항 반영:
    • 애플리케이션 요구 사항에 맞는 데이터 구조 설계.

 

데이터 모델링의 단계

a. 개념적 데이터 모델링 (Conceptual Data Modeling)

  • 무엇을 저장할 것인지 정의:
    • 현실 세계의 데이터를 추상화하여 주요 엔티티(Entity)와 관계(Relationship)를 정의.
  • 비즈니스 중심:
    • 비즈니스 요구 사항에 초점을 맞춰 데이터 구조를 설계.
- 엔티티: 사용자(User), 주문(Order), 상품(Product)
- 관계: 사용자 - 주문 (1:N), 주문 - 상품 (N:M)

 

b. 논리적 데이터 모델링 (Logical Data Modeling)

  • 어떻게 저장할 것인지 정의:
    • 엔티티를 테이블로 변환하고 속성(Attribute)과 데이터 타입을 정의.
    • 테이블 간의 관계를 정확히 정의(1:1, 1:N, N:M).

예시

 

c. 물리적 데이터 모델링 (Physical Data Modeling)

  • 어디에 저장할 것인지 정의:
    • 실제 데이터베이스에 적합한 구조로 설계.
    • 데이터 타입, 인덱스, 제약 조건, 스키마를 정의.
    • SQL 스크립트 생성.
CREATE TABLE User (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(200) UNIQUE
);

CREATE TABLE Order (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    order_date DATE,
    FOREIGN KEY (user_id) REFERENCES User(id)
);

CREATE TABLE Product (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10, 2)
);

CREATE TABLE Order_Product (
    order_id BIGINT,
    product_id BIGINT,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES Order(id),
    FOREIGN KEY (product_id) REFERENCES Product(id)
);

 

데이터 모델링의 주요 개념

a. 엔티티(Entity)

  • 데이터베이스에 저장할 대상. (예: 사용자, 주문, 상품)

b. 속성(Attribute)

  • 엔티티의 특성을 나타내는 값. (예: 이름, 이메일, 가격)

c. 관계(Relationship)

  • 엔티티 간의 연관성.
    • 1:1 관계: 한 명의 사용자 ↔ 하나의 프로필.
    • 1:N 관계: 한 명의 사용자 ↔ 여러 주문.
    • N:M 관계: 여러 주문 ↔ 여러 상품.

d. 기본 키(Primary Key)

  • 테이블의 각 행(Row)을 고유하게 식별하는 열(Column).

e. 외래 키(Foreign Key)

  • 다른 테이블의 기본 키를 참조하여 테이블 간의 관계를 정의.

f. 정규화(Normalization)

  • 중복 데이터를 제거하고 데이터 구조를 최적화.

 

 

'DB > DBMS' 카테고리의 다른 글

[JPA] DB[JPA] 정리  (0) 2025.01.24
[DBMS] SQL  (0) 2025.01.24
[DBMS] H2 (가벼운 오픈소스 관계형 DB 관리 서비스)  (0) 2025.01.23

Layered Architecture

📌 애플리케이션을 세 가지 주요 계층으로 나누어 구조화하는 방법으로 각 계층은 특정한 책임을 갖고 있으며, 계층 간에는 명확한 역할 분담이 이루어져 코드의 재사용성, 유지보수성, 확장성을 높이는 데 도움을 준다.

 

주요 특징

  1. 계층 분리:
    • 시스템을 기능별로 분리하여 모듈화.
  2. 책임 분리:
    • 각 계층은 고유한 책임과 역할을 가짐.
  3. 상호 의존성:
    • 상위 계층은 하위 계층에만 의존하며, 계층 간의 의존성을 제한.
  4. 유지보수 용이:
    • 특정 계층의 변경이 다른 계층에 최소한의 영향을 미침.

 

Layerd Architecture 개요 

 

  • 기존의 MVC 패턴에서 Controller는 역할이 무수히 많다.
    1. 요청에 대한 처리
    2. 예외처리
    3. View Template 응답 or Data 응답
    4. 비지니스 로직 처리
    5. DB 상호작용
  • 문제점
    • Controller에서 요청에 대한 모든 처리를 수행한다. 즉, 책임이 너무 많다.
    • 기능 추가, 수정, 삭제 등의 유지보수가 힘들어진다.
    • 코드의 재사용성이 떨어진다. 메서드로 분리하여도 메서드를 호출하는 중복 코드가 발생한다.

 

Layered Architecture 구조

 

  • Presentation Layer
    • 사용자의 요청을 받고 응답하는 역할을 수행한다.
    • 화면을 응답하거나 데이터를 응답하는 API를 정의한다.
  • Business Layer(Service Layer)
    • 비지니스 로직을 수행한다.
    • 요청을 해석하여 Repository Layer에 전달한다.
    • 일반적으로 하나의 비지니스 로직은 하나의 트랜잭션으로 동작한다.
  • Data Access Layer(Repository Layer)
    • 데이터베이스와 연동되어 실제 데이터를 관리한다.
  • 용어 설명
    • DTO(Data Transfer Object)
      • 계층간 데이터 전달을 위해 사용되는 객체이다.
    • Model
      • Entity
        • 추후 숙련주차에 배울 JPA와 관련이 있다.
        • JPA에서는 Entity라는 형태로 데이터를 반환한다.
    • DAO(Data Access Object)

 

계층 간 의존성

  • 상위 계층은 하위 계층에만 의존합니다.
  • 계층 간의 의존성을 단방향으로 제한하여 결합도를 낮춥니다.

계층 간 의존성 예시

Presentation → Application → Domain → Data Access

 

 

장점

  1. 유지보수성:
    • 계층별 역할이 분리되어, 특정 계층의 변경이 다른 계층에 미치는 영향을 최소화.
  2. 재사용성:
    • 서비스나 데이터 계층을 다른 애플리케이션에서도 재사용 가능.
  3. 테스트 용이성:
    • 계층 단위로 테스트가 가능하여, 단위 테스트와 통합 테스트를 쉽게 수행.
  4. 확장성:
    • 특정 계층에 새로운 기능을 추가하거나 변경하기 쉬움.

 

단점

  1. 복잡성 증가:
    • 계층 간의 통신 코드로 인해 초기 개발이 복잡해질 수 있음.
  2. 성능 문제:
    • 계층 간 호출이 많아지면 성능 저하 가능.
  3. 단순 CRUD에 과한 구조:
    • 작은 애플리케이션에서는 계층형 아키텍처가 불필요한 복잡성을 초래.

 

계층형 아키텍처를 사용할 때 적합한 경우

  1. 복잡한 비즈니스 로직:
    • 다양한 데이터 소스와 복잡한 비즈니스 규칙이 있는 애플리케이션.
  2. 협업 프로젝트:
    • 역할 분리가 명확해 팀 간 작업이 효율적.
  3. 대규모 애플리케이션:
    • 변경 사항을 쉽게 관리하고, 시스템 확장이 필요한 경우.

 

 

 

 

 

Layered Architecture 적용

 

 

 

1. Controller

  • 클라이언트의 요청을 받는 역할을 수행한다.
  • 요청에 대한 처리를 Service Layer에 전달한다.
  • Service에서 처리 완료된 결과를 클라이언트에 응답한다.
  • 사용하는 Annotation : @Controller, @RestController

2. Service

  • 사용자의 요청 사항을 처리한다.
  • DB와 상호작용이 필요한 경우, Repository Layer에게 요청한다.
  • 사용하는 Annotation: @Service

 

3. Repository

  • DB와 상호작용을 수행한다.
    • Connection 연결, 해제
    • CRUD 작업 처리
  • 사용하는 Annotation: @Repository

 

4. DTO(Data Transfer Object)

  • 계층간 데이터 전달을 위해 사용된다.
  • 요청 데이터를 처리하는 객체는 일반적으로 RequestDto로 명명한다.
  • 응답 데이터를 처리하는 객체는 일반적으로 ResponseDto로 명명한다.

 

DTO (Data Transfer Object)

📌 애플리케이션에서 계층 간 데이터 전송을 목적으로 사용하는 단순한 객체입니다.

  • 주로 데이터를 전송하기 위한 속성과 Getter/Setter 메서드만 포함하며, 비즈니스 로직을 포함하지 않습니다.

 

DTO의 주요 역할

  1. 데이터 캡슐화:
    • 데이터 구조를 명확히 정의하여 계층 간 데이터 교환을 표준화.
  2. 안전한 데이터 전달:
    • 데이터 모델(Entity)과 직접 연관되지 않아, 민감한 데이터 보호 가능.
  3. 데이터 변환 및 제한:
    • 클라이언트가 필요로 하는 데이터만 전달하도록 제한.
  4. API 설계에 유용:
    • RESTful API에서 응답(Response) 또는 요청(Request) 데이터를 정의.

 

 

DTO의 구조

DTO는 일반적으로 다음과 같은 형태로 작성됩니다:

  • 속성 필드
  • Getter/Setter 메서드
  • (선택적) 생성자, toString(), equals(), hashCode()
public class UserDTO {
    private Long id;
    private String name;
    private String email;

    // 기본 생성자
    public UserDTO() {}

    // 생성자
    public UserDTO(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // Getter와 Setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

 

DTO의 사용 예

a. 요청(Request) DTO

클라이언트가 서버에 데이터를 보낼 때 사용.

Request DTO 예제

public class CreateUserRequest {
    private String name;
    private String email;

    // Getter와 Setter
}

 

Controller

@RestController
@RequestMapping("/users")
public class UserController {

    @PostMapping
    public ResponseEntity<String> createUser(@RequestBody CreateUserRequest request) {
        // 요청 데이터 처리
        return ResponseEntity.ok("User created: " + request.getName());
    }
}

 

요청 데이터

{
    "name": "John Doe",
    "email": "john.doe@example.com"
}

 

b. 응답(Response) DTO

서버가 클라이언트에 데이터를 반환할 때 사용.

 

Response DTO 예제

public class UserResponse {
    private Long id;
    private String name;
    private String email;

    public UserResponse(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // Getter만 제공
}

 

Controller

@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
        // 서비스에서 가져온 데이터
        UserResponse response = new UserResponse(id, "John Doe", "john.doe@example.com");
        return ResponseEntity.ok(response);
    }
}

 

응답 데이터

{
    "id": 1,
    "name": "John Doe",
    "email": "john.doe@example.com"
}

 

Entity와 DTO의 차이

  Entity DTO
역할 데이터베이스 테이블과 직접 매핑. 데이터 전송용 객체.
책임 비즈니스 로직 및 데이터베이스 작업 포함. 단순히 데이터 전송 및 표현.
위치 Service, Repository 계층에서 사용. Controller와 Service 계층 간 전송.
직렬화 필요에 따라 가능. 주로 직렬화되어 전송(JSON, XML 등).
민감한 데이터 포함 가능. 민감한 데이터는 제외.

 

DTO 변환 (Entity ↔ DTO)

DTO는 Entity와 직접적으로 사용되지 않으며, 변환 과정을 거칩니다.

Service 계층에서 변환 예

@Service
public class UserService {

    public UserResponse getUserResponse(UserEntity entity) {
        return new UserResponse(
            entity.getId(),
            entity.getName(),
            entity.getEmail()
        );
    }
}

 

 

Spring Boot에서 DTO 자동 변환

Spring Boot에서 DTO 변환을 쉽게 하기 위해 ModelMapper 또는 MapStruct와 같은 라이브러리를 사용할 수 있습니다.

a. ModelMapper 예제

의존성 추가 (Maven)

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>3.1.0</version>
</dependency>

변환 코드

import org.modelmapper.ModelMapper;

@Service
public class UserService {

    private final ModelMapper modelMapper = new ModelMapper();

    public UserDTO convertToDTO(UserEntity entity) {
        return modelMapper.map(entity, UserDTO.class);
    }
}

 

DTO의 장점

  1. 데이터 보호:
    • 데이터베이스와 직접 연결된 Entity를 노출하지 않아 민감한 데이터 보호.
  2. 역할 분리:
    • Entity와 DTO를 분리하여 비즈니스 로직과 데이터 전송 책임을 명확히 구분.
  3. API 표준화:
    • 클라이언트 요청과 응답 형식을 명확히 정의 가능.
  4. 유지보수성 향상:
    • 데이터 전송 형식 변경이 Entity와 독립적으로 이루어짐.

 

결론

 

  • DTO(Data Transfer Object)는 계층 간 데이터를 주고받을 때 사용되는 단순한 객체입니다.
  • 데이터 캡슐화, 보안성 강화, API 설계 표준화 등의 장점으로, 특히 Spring MVC와 RESTful API 설계에서 널리 사용됩니다.
  • DTO를 활용하여 애플리케이션의 구조를 더 유연하고 유지보수 가능하게 설계할 수 있습니다.

정적 리소스

📌 웹 애플리케이션에서 변하지 않는 파일들을 의미한다. 예를 들어, HTML, CSS, JavaScript, 이미지 파일들(JPG, PNG, GIF) 등이 정적 리소스에 해당한다.

  • 정적인 HTML, CSS, JS, Image 등을 변경 없이 그대로 반환한다.

 

정적 리소스의 특징

  1. 고정된 내용:
    • 요청이 올 때마다 동일한 콘텐츠를 제공합니다.
    • 실행되거나 동적으로 변환되지 않습니다.
  2. 캐싱 가능:
    • 정적 리소스는 자주 요청되므로 브라우저에서 캐싱하여 성능을 최적화할 수 있습니다.
  3. 서버 부담 감소:
    • 서버는 정적 리소스를 변환하거나 처리하지 않으므로, 부하가 적습니다.
  4. 브라우저에서 직접 렌더링:
    • 클라이언트(브라우저)가 직접 처리할 수 있는 파일 형식으로 제공됩니다.

 

 

Spring Boot에서 정적 리소스 제공

  • Spring Boot는 기본적으로 정적 리소스를 제공하기 위한 구조를 내장하고 있습니다.

1. 정적 리소스 경로

Spring Boot는 다음 디렉토리에서 정적 리소스를 자동으로 로드합니다:

디렉토리 설명
/static src/main/resources/static에 있는 파일을 정적 리소스로 제공.
/public src/main/resources/public에 있는 파일을 정적 리소스로 제공.
/resources src/main/resources에 있는 파일.
/META-INF/resources JAR 파일의 META-INF 디렉토리 내부의 파일.

 

2. 정적 리소스 예제

 

 

 

정적 리소스와 동적 리소스 비교

  정적 리소스 동적 리소스
변경 여부 고정된 콘텐츠 (HTML, CSS, JS, 이미지 등). 요청에 따라 내용이 동적으로 생성됨.
처리 방식 서버가 파일 그대로 응답. 서버가 요청 데이터를 처리하여 응답 생성.
요청-응답 속도 빠름 (파일 그대로 반환). 비교적 느림 (처리 후 반환).
예시 HTML, CSS, JS, 이미지 파일. REST API 응답, 템플릿 엔진 출력.

 

 

정적 리소스와 컨트롤러 경로 충돌

Spring Boot는 기본적으로 정적 리소스를 우선 처리합니다.
예:

  • /index.html 정적 파일이 있으면, 해당 파일을 반환.
  • /index를 처리하는 컨트롤러가 있다면, 정적 리소스 대신 컨트롤러가 처리.

컨트롤러 우선 처리

application.properties에서 다음 설정으로 컨트롤러를 우선 처리하도록 변경 가능합니다:

spring.mvc.static-path-pattern=/**

 

 

정적 리소스 최적화

  1. Gzip 압축:
    • 정적 리소스를 Gzip으로 압축하여 전송 속도를 개선.
    properties
     
     
  2. Content Delivery Network (CDN):
    • 정적 리소스를 CDN에 배포하여 전 세계적으로 빠르게 제공.
  3. 캐싱:
    • 브라우저 캐싱을 활용하여 불필요한 네트워크 요청을 줄임.
server.compression.enabled=true
server.compression.mime-types=text/html,text/xml,text/plain,text/css,application/javascript

 

 

요약

  • 정적 리소스는 서버가 변경 없이 그대로 반환하는 콘텐츠입니다.
  • Spring Boot는 /static, /public 등 기본 경로에서 정적 리소스를 자동으로 제공합니다.
  • 정적 리소스는 속도가 빠르고 서버 부하가 적으므로, 이미지, CSS, JS 등의 파일 전송에 최적입니다.
  • 필요 시 경로와 캐싱 설정을 커스터마이징할 수 있습니다.
  • Spring Boot의 정적 리소스 경로
    • 아래 경로들에 정적 리소스가 존재하면 서버에서 별도의 처리 없이 파일 그대로 반환된다.
    1. /static
    2. /public
    3. /META-INF/resources
    4. src/main/resources
      1. /static

Spring Boot Directory 구조

  • src/main/resources/static/hello/world.html 디렉토리 구조라면
    • http://localhost:8080/hello/world.html URL로 리소스에 접근이 가능하다.
    • /static 대신 /public 혹은 /META-INF/resources 도 사용 가능하다.

 

View Template (동적 리소)

📌 웹 애플리케이션에서 서버가 클라이언트에 응답으로 제공할 HTML 페이지를 동적으로 생성하기 위해 사용하는 템플릿 파일입니다. 데이터를 표시하기 위해 동적인 콘텐츠와 정적인 HTML 구조를 결합하는 데 사용됩니다.

  • Spring에서는 Thymeleaf, JSP와 같은 템플릿 엔진을 사용해 View Template을 작성할 수 있고, View Template은 서버에서 데이터를 받아 이를 HTML 구조에 맞게 삽입한 후, 최종적으로 클라이언트에게 전송되는 HTML 문서로 변환하여 사용자에게 동적으로 생성된 웹 페이지를 제공한다.
  • 📌 View Template
    • View Template은 Model을 참고하여 HTML 등이 동적으로 만들어지고 Client에 응답된다.
    • Spring Boot는 기본적으로 View Template 경로(src/main/resources/templates)를 설정한다.
    • build.gradle에 Thymeleaf 의존성을 추가하면 ThymeleafViewResolver와 필요한 Spring Bean들이 자동으로 등록된다. 
  • SSR(Server Side Rendering)을 사용할 때 View가 반환된다.

View Template의 역할

  • 정적 HTML과 동적 데이터 결합:
    • 서버에서 HTML 파일에 동적 데이터를 삽입하여 사용자 맞춤 콘텐츠를 생성.
  • 서버 사이드 렌더링 (SSR):
    • 브라우저에 표시될 HTML을 서버에서 렌더링하고 클라이언트로 전송.
  • UI 레이아웃 관리:
    • 레이아웃, 반복 구조, 조건부 표시 등과 같은 동적 UI 요소를 효율적으로 관리.

 

Spring MVC에서 View Template

Spring MVC에서는 Model-View-Controller 패턴을 기반으로 View Template 엔진을 사용하여 HTML을 생성합니다.

Spring MVC에서의 동작 흐름

  1. Controller:
    • 클라이언트의 요청을 처리하고, 데이터를 Model 객체에 담아 View로 전달.
  2. View Template:
    • Model 데이터를 기반으로 HTML을 렌더링.
  3. Client:
    • 완성된 HTML 페이지를 클라이언트로 전송하여 브라우저에 표시.

 

pring Boot에서 지원하는 View Template 엔진

Thymeleaf Spring Boot에서 가장 널리 사용되는 템플릿 엔진. 자연스러운 HTML과의 통합 제공.
JSP Java 기반의 전통적인 템플릿 엔진. Servlet Container에서 실행.
Freemarker 강력한 기능의 템플릿 엔진. 다양한 커스터마이징 가능.
Mustache 간단하고 가벼운 템플릿 엔진. JavaScript에서도 동일 문법 사용 가능.
Groovy Templates Groovy 언어 기반의 템플릿 엔진.

 

 

Thymeleaf 예제 (Spring Boot 기본 설정)

a. Maven 의존성 추가

Spring Boot Starter를 사용하면 Thymeleaf가 기본적으로 포함됩니다:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

 

b. 프로젝트 구조

 

c. Controller 코드

@Controller
public class HomeController {

    @GetMapping("/")
    public String home(Model model) {
        model.addAttribute("title", "Welcome to Thymeleaf");
        model.addAttribute("message", "Hello, Thymeleaf!");
        return "home"; // home.html을 렌더링
    }
}

 

d. Thymeleaf View Template (home.html)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title th:text="${title}">Default Title</title>
</head>
<body>
    <h1 th:text="${message}">Default Message</h1>
</body>
</html>

 

결과

  1. 클라이언트가 /로 요청.
  2. Controller가 Model에 데이터를 담아 home.html로 전달.
  3. Thymeleaf가 데이터를 HTML에 삽입.
  4. 클라이언트로 다음과 같은 HTML 응답이 전송:
<!DOCTYPE html>
<html>
<head>
    <title>Welcome to Thymeleaf</title>
</head>
<body>
    <h1>Hello, Thymeleaf!</h1>
</body>
</html>

 

 

@Controller의 응답으로 String을 반환하는 경우

  • @ResponseBody 가 없으면 View Resolver가 실행되며 View를 찾고 Rendering한다.
@Controller
public class ViewTemplateController {
	
	@RequestMapping("/response-view")
  public String responseView(Model model) {
      // key, value
      model.addAttribute("data", "sparta");

      return "thymeleaf-view";
  }
	
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
    <h1>Thymeleaf</h1>
    <h2 th:text="${data}"></h2>
</body>
</html>

 

ex) http://localhost:8080/response-view

  • @ResponseBody 가 있으면 HTTP Message Body에 return 문자열 값이 입력된다.

Postman

 

반환 타입이 void인 경우

  • 잘 사용하지 않는다.
  • @Controller + (@ResponseBody, HttpServletResponse, OutputStream)과 같은 HTTP Message Body를 처리하는 파라미터가 없으면 RequestMapping URL을 참고하여 View Name으로 사용한다.
@Controller
public class ViewTemplateController {
	
	// thymeleaf-view.html 과 Mapping된다.
  @RequestMapping("/thymeleaf-view")
  public void responseViewV2(Model model) {
      model.addAttribute("data", "sparta");
  }
	
}

 

  • 예시와 같은 경우에는 viewTemplate(viewName)을 RequestMapping URL 주소로 찾는다.

 

 

HTTP Message Body

📌 HTTP Message Body는 HTTP 요청(Request) 또는 응답(Response)에서 실제 데이터를 담는 부분입니다.

  • REST API를 만드는 경우 Server에서 Client로 HTML을 전달하는 방식이 아닌 HTTP Message Body에 직접 Data를 JSON 형식으로 담아 전달한다.
  • 정적 HTML, View Template 또한 HTTP Message Body에 담겨서 전달된다. 현재 설명하는 Response의 경우는 정적 HTML, View Template을 거치지 않고 직접 HTTP Response Message를 만들어 전달하는 경우를 말하는것.

 

HttpServletResponse 사용

@Controller
public class ResponseBodyController {
	
	@GetMapping("/v1/response-body")
	public void responseBodyV1(
														HttpServletResponse response
													) throws IOException {
		
		response.getWriter().write("data");
	
	}
}

Postman

  • Response Body에 data 라는 문자열이 입력되어 응답된다.
  • 기존 Servlet을 다룰 때 코드와 형태가 같다.

 

ResponseEntity<> 사용

@GetMapping("/v2/response-body")
public ResponseEntity<String> responseBodyV2() {
		
	return new ResponseEntity<>("data", HttpStatus.OK);
}

Postman

  • Response Body에 data라는 문자열과 HttpStatus.OK에 해당하는 상태 코드를 반환한다.
  • ResponseEntity는 HttpEntity 를 상속받았다.
    • HttpEntity는 HTTP Message의 Header, Body 모두 가지고 있다.

 

@ResponseBody(TEXT, JSON) 사용

@Data
@NoArgsConstructor // 기본 생성자
@AllArgsConstructor // 전체 필드를 인자로 가진 생성자
public class Tutor {

    private String name;
    private int age;

}
// TEXT 데이터 통신
@ResponseBody
@GetMapping("/v3/response-body-text")
public String responseBodyText() {
		
	return "data"; // HTTP Message Body에 "data"
}

 

Postman

// JSON 데이터 통신
@ResponseBody
@GetMapping("/v3/response-body-json")
public Tutor responseBodyJson() {
		
	Tutor tutor = new Tutor("wonuk", 100);
		
	return tutor; // HTTP Message Body에 Tutor Object -> JSON
}

Postman

  • View를 사용하는 것이 아닌 HTTP Message Converter를 통해 HTTP Message Body를 직접 입력할 수 있다. → ResponseEntity와 같음
  • @ResponseStatus 를 사용하여 상태 코드를 지정할 수 있다.

 

@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/v4/response-body")
public Tutor responseBodyV4() {
		
	Tutor tutor = new Tutor("wonuk", 100);
		
	return tutor;
}

Postman

  • 단, 응답 코드를 조건에 따라서 동적으로 변경할 수는 없다.

ex) 1번의 경우 OK, 2번의 경우 Created

 

 

ResponseEntity<Object>(JSON)

@ResponseBody
@GetMapping("/v5/response-body")
public ResponseEntity<Tutor> responseBody() {
		
	Tutor tutor = new Tutor("wonuk", 100);
	
	return new ResponseEntity<>(tutor, HttpStatus.OK);
}

Postman

  • ResponseEntity<>두 번째 파라미터에 Enum을 사용하여 상태 코드를 바꿀 수 있다.
  • HTTP Message Converter를 통하여 JSON 형태로 변환되어 반환된다.
  • 동적으로 응답 코드를 변경할 수 있다.
@ResponseBody
@GetMapping("/v5/response-body")
public ResponseEntity<Tutor> responseBody() {
		
	Tutor tutor = new Tutor("wonuk", 100);
	
	if (조건) {
		return new ResponseEntity<>(tutor, HttpStatus.OK);
	} else {
		return new ResponseEntity<>(tutor, HttpStatus.BAD_REQUEST);
	}
	
}
  • HttpEntity를 상속받았다.

 

 

 

정리

 

Client에서 Server로 Data를 전달하는 세가지 방법

  1. GET - Query Param, Query String

ex) http://localhost:8080/tutor?name=wonuk&age=100

  • 사용하는 어노테이션
    • @RequestParam, @ModelAttribute
  1. POST - HTML Form(x-www-form-urlencoded)
POST /form-data
content-type: application/x-www-form-urlencoded

**key1=value1&key2=value2**
  • 사용하는 어노테이션
    • @RequestParam, @ModelAttribute
  1. HTTP Request Body

ex) 데이터(JSON, TEXT, XML 등)를 직접 HTTP Message Body에 담아서 사용한다.

  • 사용하는 어노테이션
    • @RequestBody

 

 

Server(Spring)에서 HTTP 응답을 Client에 전달하는 세가지 방법

  1. 정적 리소스
    • 정적인 HTML, CSS, JS, Image 등을 변경없이 그대로 반환한다.

  1. View Template
    • SSR(Server Side Rendering)을 사용할 때 View가 반환된다.
    • 사용하는 어노테이션
      • @Controller
       

 

  1. HTTP Message Body
    • 응답 데이터를 직접 Message Body에 담아 반환한다.
    • 사용하는 어노테이션
      • @ResponseBody, ResponseEntity<Object>
       

 

  • 요청
    • @RequestParam, @ModelAttribute, @RequestBody
  • 응답
    • 정적 리소스, View Template(@Controller), @ResponseBody, ResponseEntity<Object>

'Back-End (Web) > Spring' 카테고리의 다른 글

[Spring] Spring의 핵심 개념  (0) 2024.12.24
[Spring] Layered Architecture  (2) 2024.12.20
[Spring] HTTP Message Body & TEXT  (0) 2024.12.18
[Spring] @RequestParam & @ModelAttribute  (0) 2024.12.17
[Spring] Request Mapping  (1) 2024.12.16

트러블 슈팅

1. 배경

  • 웹 개발 숙련에 입문하였다.
  • 사용 기술은 jpa와 쿠키,세션이 추가되었다.
  • 웹 개발의 기초에서 진행하였던 일정표 프로젝트를 개량하는 것이 발제였다.

 

2. 발단

  • 처음은 아니였던 만큼 머리속에 웹 개발의 전재라인이 떠올랐던 점이 크게 작용했다.
  • 확실히 저번보다는 편하게 진행이 가능했다는 점이 조금은 성장했다는 것을 느낄 수 있었다.
  • 하지만 문제가 없지는 않았다.
  • 프론트와 함께 쓰는것을 목표로 했다보니, 자연스럽게 프로젝트도 프론트를 구성했었는데, 튜터님의 조언이 백엔드에 치중해서 코드를 작성해보라는 내용이 있다는 것을 너무 늦게 파악했다..
  • 코드적인 문제로는, 중간에 프론트에서 데이터를 한번에 2가지 servlet으로 전송하려했는데 그게 좀처람 잘 되지 않았다.
  • 그 외에도 세션을 사용하려다가 생기는 오류가 많아 애를 먹었다.

 

3. 전개&위기

  • 여전히 문제는 저 2개였다. controller와 service, 전에는 controller와 repository에 모든 기능을 다 넣었지만 이번에는 service를 추가해보는 것이 좋을 것 같다는 조언에 의해 나눠서 처리했다.
  • 이번에는 확실히 처리하는 파일과 저장하는 파일이 따로 존재하다보니 코드의 수정에서 굉장히 편하다는 것을 느낄 수 있었고, 일단 코드를 굳이 엄한데에서 안 찾아도 되었다는 점이 굉장히 편했다.
  • 다만, 세션의 filter의 경우 처음하는 부분이다보니 많이 해매였던 것 같다. 
  • 코드 자체가 인증, 인가라고는 하지만 막상 해보기 전까지는 어떤 방식의 구현인지 이해가 정확히되지 않았다는 점이 컸던 것 같다.

 

5. 절정

  • 일단 처음에는 받았던 강의 내용을 그대로 사용해 보다가 gpt를 통해 업그래드를 진행해 보았다. 내용은 간단히 받은 세션과 내용의 일치를 통한 간단한 절차를 구현했지만, 뭔가 많이 부족해보인다.
  • 대부분의 문제의 경우 bean이나 다양한 파트에서 자동으로 지원해 준다는 점에서 간편하게 구현이 가능했고 처음에 많이 해메이던 부분은 한번 하고나니 확실히 익숙해지는 것이 느껴졌다.

 

6. 결말

  • 이번에는 계속 집 수리로 인해 시간을 너무 많이 잃었던 점이 코드를 작성하지 못한 큰 이유가 되었다.
  • 강의도 정말 일단 가능한곳까지는 해보자는 생각으로 추가학습과 주말도 사용했지만, 생각보다 기공분들이 너무 많이 찾아오셨고 체크를 담당하고 설명을 듣고 부모님께 전달하는 시간이 너무 길었다.
  • 앞으로도 계속 이럴 수 없다는 생각이 먼저들었다. 
  • 동생과 분담하는 방식으로 일정을 조절해야지, 안그러면 팀프로젝트에서도 민폐를 끼칠 것 같다.
  • 코드는 단순히 있는 코드를 변형하는 방식이라 jpa자체는 엄청 힘들지는 않았지만, 처음해보는 세션로그인은 조금 해메였던 것 같다.
  • 처음에 걱정하던 부분은 머리속에 개발 로드맵이 어느정도 구성되고 나니 좀 불안이 덜어진 것 같다.
  • 다만 postman과 테스트코드가 없다는 점에서 앞으로도 문제는 해결할 필요가 있어보인다.
  • 프론트를 일부러 같이 가져가려고 했지만, 그렇게 좋은 방법은 아니였다는 조언이 있었던 만큼, 차라리 프론트를 만들 시간에 다른 백엔드를 좀 더 가공하려 하는 방식으로 바꿔야할 것 같다.

'Project > Spring' 카테고리의 다른 글

Spring Querydsl 과제 회고  (1) 2025.01.27
[Spring] IOC & DI  (0) 2025.01.23
스프링 NEWSFEED 협업 프로젝트  (1) 2024.12.27
일정표를 만들어 보자!  (2) 2024.12.09
[ Spring ] 쇼핑몰 프로젝트 회고  (0) 2024.11.25

JSON

📌 데이터를 표현하기 위한 가볍고 간단한 텍스트 기반의 데이터 교환 형식입니다. 키-값 쌍으로 데이터를 구조화하며, 사람과 기계가 읽고 쓰기 쉽도록 설계되었습니다. 웹 애플리케이션과 서버 간의 데이터 교환에서 널리 사용됩니다.

  • Json은 @RestController 에서 가장 많이 사용되는 데이터 형식이다. 현재 대부분의 API는 Request, Response 모두 JSON 형태로 통신한다.
  • Json 형태로 Data를 전송할 때는 Request Header의 content-type이 꼭 application/json 이여야 한다.

 

JSON의 특징

  1. 가독성:
    • 간단한 문법으로 사람이 읽기 쉽습니다.
  2. 경량성:
    • XML에 비해 데이터 표현이 간결하여 네트워크 트래픽이 적습니다.
  3. 언어 독립성:
    • 대부분의 프로그래밍 언어에서 JSON을 쉽게 생성하고 파싱할 수 있습니다.
  4. 구조화된 데이터 표현:
    • 객체, 배열, 숫자, 문자열 등 다양한 데이터 타입을 지원합니다.

 

JSON의 데이터 타입

문자열 "hello" 큰따옴표로 묶인 텍스트.
숫자 123, 45.67 정수 및 실수 지원.
불리언 true, false 논리 값.
객체 { "key": "value" } 키-값 쌍의 집합.
배열 [1, 2, 3] 여러 값의 순서 있는 리스트.
null null 값이 없음.

 

JSON 처리 (Java 예제)

a. Java 객체 → JSON 변환

Spring Boot에서 Jackson 라이브러리를 사용하여 JSON 데이터를 처리할 수 있습니다.

import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        // 객체 생성
        User user = new User("John", 30);

        // 객체를 JSON으로 변환
        String json = objectMapper.writeValueAsString(user);
        System.out.println(json);
    }
}

class User {
    private String name;
    private int age;

    // Constructor, Getters, Setters
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters and Setters
}

 

JSON과 XML 비교

  JSON XML
가독성 간결하고 읽기 쉬움 태그가 많아 다소 복잡.
데이터 크기 작음 크기가 큼.
파싱 속도 빠름 느림.
데이터 표현 객체 및 배열을 자연스럽게 표현 객체와 배열 표현이 불편함.

 

 

JSON의 장점

  1. 간단하고 가볍다:
    • 데이터 표현이 직관적이고 XML에 비해 크기가 작음.
  2. 언어 독립적:
    • JavaScript뿐만 아니라 Python, Java, PHP 등 다양한 언어에서 지원.
  3. API 표준:
    • RESTful API에서 사실상의 표준 형식으로 사용.

 

JSON 요청

1. HttpServletRequest 사용

더보기
@Data
public class Tutor {
	private String name;
	private int age;
}

@RestController
public class JsonController {
	
	private ObjectMapper objectMapper = new ObjectMapper();

	@PostMapping("/v1/request-body-json")
	public void requestBodyJsonV1(
				HttpServletRequest request, 
				HttpServletResponse response
	) throws IOException {

		// request body message를 Read
		ServletInputStream inputStream = request.getInputStream();
		// UTF-8 형식의 String으로 변환한다.
		String requestBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

		// String requestBody를 ObjectMapper를 사용하여 변환 "{\"name\":\"wonuk\", \"age\":10}"
		Tutor tutor = objectMapper.readValue(requestBody, Tutor.class);
		
		// 응답
		response.getWriter().write("tutor" + tutor);
	}
}
  • Postman
    • Content-Type 헤더 확인 : application/json

 

  • HttpServletRequest를 사용하여 HTTP Message Body 데이터를 Read하여 문자로 변환한다.
  • 문자로 만들어진 JSON을 Jackson 라이브러리의 objectMapper를 사용하여 Object로 변환

 

 

 

 

 

 

 

2. @RequestBody 사용

더보기
@RestController
public class JsonController {
	
	private ObjectMapper objectMapper = new ObjectMapper();

	@PostMapping("/v2/request-body-json")
  public String requesBodytJsonV2(@RequestBody String requestBody) throws IOException {

      Tutor tutor = objectMapper.readValue(requestBody, Tutor.class);

      return "tutor.getName() = " + tutor.getName() + "tutor.getAge() = " + tutor.getAge();
  }
    
}
Postman

@RequestBody를 사용하여 HTTP Request Body의 Data에 접근한다.

3. ObjectMapper 제거

더보기
@RestController
public class JsonController {
	
	@PostMapping("/v3/request-body-json")
	public String requestBodyJsonV3(@RequestBody Tutor tutor) {
		
		Tutor requestBodyTutor = tutor;

		return "tutor = " + requestBodyTutor;
	}
}
Postman

 

위 Controller가 동작하는 이유는 무엇인가요?
  • @RequestBody
    • @RequestBody 어노테이션을 사용하면 Object를 Mapping할 수 있다.
    • HttpEntity<>, @RequestBody를 사용하면 HTTPMessageConverter가 Request Body의 Data를 개발자가 원하는 String이나 Object로 변환해준다.
    • JSON to Object의 Mapping 또한 가능하다.
      • MappingJackson2HttpMessageConverter 의 역할
      • 쉽게 설명하면 HTTP Message Converter가 ObjectMapper를 대신 실행한다.

4. @RequestBody는 생략할 수 없다.

더보기

@RequstParam, @ModelAttribute는 생략이 가능하다.

 

@Slf4j
@RestController
public class JsonController {
	
	@PostMapping("/v4/request-body-json")
  public String requestBodyJsonV4(Tutor tutor) { // @RequestBody 생략시 @ModelAttribute가 된다.

      Tutor requestBodyTutor = tutor;

      return "tutor.getName() = " + requestBodyTutor.getName() + " tutor.getAge() = " + requestBodyTutor.getAge();
  }
	
}
Postman
  • 생략하면 @ModelAttribute가 된다.
    • 요청 파라미터를 처리하도록 설정된다.
  • Request Header의 contentType은 꼭 application/json 이여야 한다.
    • 위 설정 정보를 기반으로 MessageConverter가 실행된다.

 

5. HttpEntity 사용

더보기
@RestController
public class JsonController {
	
	@PostMapping("/v5/request-body-json")
  public String requestBodyJsonV5(
          HttpEntity<Tutor> httpEntity
  ) {
      // 값을 꺼내서 사용해야한다!
      Tutor tutor = httpEntity.getBody();

      return "tutor.getName() = " + tutor.getName() + " tutor.getAge() = " + tutor.getAge();
  }
	
}

 

Postman
  • HttpEntity<Tutor>
    • Generic Type으로 Tutor가 지정되어 있기 때문에 해당 Class로 반환된다.

 

6. @ResponseBody

더보기
@Controller
public class JsonController {
	
	@ResponseBody // @RestController = @Controller + @ResponseBody
	@PostMapping("/v6/request-body-json")
    public Tutor requestJson(@RequestBody Tutor tutor) {
        return tutor;
  }
	
}
Postman

 

  • View를 조회하지 않고 Response Body에 Data를 입력해서 직접 반환한다.
  • 요청 뿐만이 아니라 응답에도 HttpMessageConverter가 동작한다.
    • MaapingJackson2HttpMessageConverter 적용
    • 응답 객체인 Tutor가 JSON으로 변환되어 반환된다.
  • HttpEntity를 사용해도 된다.

 

요약

  1. 요청 데이터는 @RequestBody를 사용해서 바인딩 하면 된다.
  2. @RequestBody 는 생략이 불가능하다.
    • @ModelAttribute가 적용되기 때문
  3. HttpMessageConverter 가 요청 응답 데이터를 모두 변환할 수 있다.
    • JSON은 MappingJackson2HttpMessageConverter 를 사용한다.
    • Request Header의 Content-Type은 application/json 이어야 한다.
      • Header로 어떤 Converter가 동작할지 판별한다.

 

 

쿼리 파라미터(Query Parameter)

📌 HTTP 요청 URL의 일부로, Key-Value 형태로 데이터를 전달하는 방법

  • 쿼리 파라미터는 URL 경로 끝에 **?**로 시작하며, 여러 개의 파라미터는 **&**로 구분됩니다.
구조
<URL>?<key1>=<value1>&<key2>=<value2>

 

특징

  1. 클라이언트가 서버로 데이터를 전달할 때 사용.
  2. GET 요청에서 주로 사용되며, 요청 URL에 데이터를 포함.
  3. 데이터가 URL에 노출되므로 민감한 정보를 보내는 데 적합하지 않음.
  4. 여러 파라미터를 전달할 수 있음.

 

 

HTML 폼 데이터(Form Data)

📌 HTML 폼(Form)을 통해 서버로 전달되는 Key-Value 형태의 데이터입니다.

  • 폼 데이터는 주로 POST 요청의 HTTP 요청 본문(body)에 포함됩니다.

 

구조

HTML 폼을 통해 데이터를 보낼 때, Key-Value 쌍으로 데이터를 전달합니다. 데이터는 HTTP 요청 본문에 포함되거나, GET 요청의 경우 쿼리 파라미터로 전송됩니다.

 

특징

  1. POST 요청에서 주로 사용되며, 데이터가 HTTP 요청 본문에 포함.
  2. GET 요청에서는 폼 데이터가 쿼리 파라미터로 전달.
  3. 데이터를 숨길 수 있으나, 민감한 데이터는 암호화(HTTPS) 필요.
  4. 주로 사용자 입력 데이터를 서버로 전달할 때 사용.

 

HTML 폼 예제

<form action="/submit" method="POST">
    <label for="name">Name:</label>
    <input type="text" id="name" name="name">
    
    <label for="email">Email:</label>
    <input type="email" id="email" name="email">
    
    <button type="submit">Submit</button>
</form>

 

POST 요청 본문 예시:

name=John&email=john.doe@example.com

 

Spring 컨트롤러에서 처리:

@PostMapping("/submit")
public String handleForm(
    @RequestParam String name,
    @RequestParam String email) {
    return "Name: " + name + ", Email: " + email;
}

 

 

쿼리 파라미터와 폼 데이터의 차이

  쿼리 파라미터 (Query Parameter) HTML 폼 데이터 (Form Data)
데이터 위치 URL 경로 끝 (쿼리 스트링) HTTP 요청 본문(body) 또는 URL 쿼리
주요 HTTP 메서드 GET POST (또는 GET)
노출 여부 데이터가 URL에 노출됨 POST 요청 시 URL에 노출되지 않음
사용 목적 짧고 간단한 데이터 전달 사용자 입력 데이터 전송
데이터 크기 제한 브라우저에 따라 URL 길이 제한 (2,000~8,000자) 요청 본문에 데이터를 보내므로 제한 없음
보안성 민감한 데이터 전달에 부적합 POST로 전달 시 상대적으로 안전 (HTTPS 권장)

 

 

예제 비교

쿼리 파라미터

GET /search?keyword=spring&category=books

 

폼 데이터

POST /submit
Content-Type: application/x-www-form-urlencoded
Content-Length: 27

name=John&email=john.doe@example.com

 

결론

  • 쿼리 파라미터는 주로 GET 요청에서 사용되며, URL에 간단한 데이터를 전달.
  • 폼 데이터는 주로 POST 요청에서 사용되며, 대량의 사용자 입력 데이터를 서버로 보낼 때 적합.

 

 

 

@RequestParam

📌 HTTP 요청의 쿼리 파라미터(Query Parameter)나 HTML 폼 데이터(Form Data)를 컨트롤러 메서드의 매개변수로 매핑하는 데 사용됩니다. 주로 클라이언트가 요청 시 전달하는 Key-Value 형식의 데이터를 처리할 때 사용됩니다.

  • URL에서 파라미터 값과 이름을 함께 전달하는 방식으로 주로 HTTP 통신 Method 중 GET 방식의 통신을 할 때 많이 사용한다. @Requestparam을 사용하면 요청 파라미터 값에 아주 쉽고 간편하게 접근(Parameter Binding)할 수 있습니다.
  • ?뒤에 오는 URL을 Query String, Query Parameter, Request Param이라 합니다.
  • GET 요청에서 쿼리 파라미터를 처리하거나, POST 요청에서 폼 데이터를 처리하는 데 사용됩니다.

 

사용 목적

  • 쿼리 파라미터(Query Parameter):
    • URL에 포함된 Key-Value 형식의 데이터를 매핑.
    • 예: GET /api/search?keyword=spring
  • 폼 데이터(Form Data):
    • HTML 폼을 통해 전송된 데이터를 매핑.
    • 예: POST 요청 본문에 name=John&email=john.doe@example.com 데이터가 포함.

주요 속성

  설명 기본값
name 요청 파라미터의 이름을 명시. 매개변수 이름과 동일
required 요청 파라미터가 필수인지 여부. true
defaultValue 요청 파라미터가 없을 때 사용할 기본값. 없음

@RequestParam과 다른 어노테이션 비교

어노테이션 설명
@RequestParam 요청의 쿼리 파라미터 또는 폼 데이터를 매핑.
@PathVariable URL 경로의 변수 값을 매핑.
@RequestBody HTTP 요청 본문(body)의 데이터를 매핑 (JSON, XML 등).

 

주의 사항

  1. 필수 속성 처리:
    • 기본적으로 @RequestParam은 요청 파라미터가 없으면 에러를 발생시킵니다.
      필요한 경우 required = false를 명시하거나, defaultValue를 설정하세요.
  2. 타입 변환:
    • Spring은 문자열 값을 기본적으로 매핑하며, 필요한 경우 자동으로 타입 변환을 지원합니다.
      단, 변환할 수 없는 타입이 지정되면 예외가 발생합니다.

 

@Slf4j
@Controller
public class RequestParamControllerV2 {

	@ResponseBody
	@GetMapping("/v1/request-param")
	public String requestParamV1 (
					@RequestParam("name") String userName,
					@RequestParam("age") int userAge													
	) {
		// logic
		log.info("name={}", userName);
    log.info("age={}", userAge);
		return "success";
	}

}
  1. @Controller + @ResponseBody
    • View를 찾는 것이 아니라 ResponseBody에 응답을 작성한다(=@RestController)
  2. @RequestParam
    • 파라미터 이름으로 바인딩한다.
  3. @RequestParam(”속성값”)
    • 속성값이 파라미터 이름으로 매핑된다.

Postman
출력 결과

 

“속성값”과 변수명이 같으면 생략이 가능하다.

ex) @RequestParam("name") String name

// GET http://localhost:8080/v2/request-param?name=sparta&age=100
@ResponseBody
@GetMapping("/v2/request-param")
public String requestParamV2 (
				@RequestParam String name,
				@RequestParam int age													
) {
	// logic
	log.info("name={}", name);
  log.info("age={}", age);
	return "success";
}

 

 

 

 

@RequestParam 사용법

1. 어노테이션, 속성값 모두 생략

  • @RequestParam은 생략이 가능하다.
// GET http://localhost:8080/v3/request-param?name=sparta&age=100
@ResponseBody
@GetMapping("/v3/request-param")
public String requestParamV3 (
				String name,
				int age													
) {
	// logic
	log.info("name={}", name);
  log.info("age={}", age);
	return "success";
}
  • 생략하면 @RequestParam(required=false) 필수 여부 속성이 default로 설정된다.
  • 단, 요청 파라미터와 이름이 완전히 같아야 한다.
  • 단순 타입(int, String, Integer 등)이어야 한다.

 

위의 방식은 권장하지 않습니다. 명시적으로 표시되어있지 않으면 팀의 협의가 있지 않는 경우 다른 개발자들에게 혼동을 주게 됩니다. 최소 @RequestParam String name 속성 값 생략 형태를 쓰면 됩니다.

 

2. required 속성 설정

  • 파라미터의 필수 값을 설정한다.
  • API 스펙을 규정할 때 사용한다.
@ResponseBody
@GetMapping("/v4/request-param")
public String requestParam (
				@RequestParam(required = true) String name, // 필수
				@RequestParam(required = false) int age	// 필수가 아님										
) {
	// logic
	log.info("name={}", name);
  log.info("age={}", age);
	return "success";
}

 

  • @RequestParam을 사용하면 기본 Default값은 True이다.
    • True로 설정된 파라미터 값이 요청에 존재하지 않으면 400 BadRequest(클라이언트 측 에러)
  • Exception이 발생하지 않는 경우

ex) http://localhost:8080/v4/request-param?name=sparta&age=100

  • Exception이 발생하는 경우

ex) http://localhost:8080/v4/request-param?age=100

 

ex) http://localhost:8080/v4/request-param

  • required = false 설정이 되어있으면 해당 파라미터는 없어도 된다.
    • 주의! http://localhost:8080/v4/request-param?name=sparta 요청한다면?

  • 500 Error가 발생한다.
  • int Type에는 null을 넣을 수 없다. 0이라도 들어가야 한다.

 

따라서 보통 null을 허용하는 Integer로 사용하거나 default 옵션을 사용한다.

@ResponseBody
@GetMapping("/v4/request-param")
public String requestParam (
				@RequestParam(required = true) String name, // 필수
			  @RequestParam(required = false) Integer age										
) {
	// logic
	log.info("name={}", name);
  log.info("age={}", age);
	return "success";
}

 

  • 파라미터 Key값만 있고 Value가 없는 경우 http://localhost:8080/request-param?name=

  • null과 빈 문자열 “”은 다르다!
  • 위 형태는 빈 문자열 “” 로 인식한다, True지만 통과가 되어버린다. 주의해야 한다.

 

 

3. default 속성 적용

  • 파라미터의 기본 값을 설정한다.
@ResponseBody
@GetMapping("/v5/request-param")
public String requestParam (
				@RequestParam(required = true, defaultValue = "sparta") String name,
		    @RequestParam(required = false, defaultValue = "1") int age											
) {
	// logic
	log.info("name={}", name);
  log.info("age={}", age);
	return "success"	
}

 

  • name Parameter 의 값이 없으면 기본적으로 “sparta”으로 설정한다

ex) http://localhost:8080/v5/request-param?age=100

  • age Parameter의 값이 없으면 기본적으로 1 으로 설정한다.

ex) http://localhost:8080/v5/request-param?name=wonuk

ex) http://localhost:8080/v5/request-param

  • 주의! defaultValue 속성을 설정하게 되면 “” 빈 문자열의 경우에도 기본 값이 설정된다.

ex) http://localhost:8080/v5/request-param?name&age

 

4. Map 사용

  • Parameter를 Map형태로 조회가 가능하다.
@ResponseBody
@GetMapping("/v6/request-param")
public String requestParamV6(
        @RequestParam Map<String, String> map
) {
    // logic
    log.info("name={}", map.get("name"));
    log.info("age={}", map.get("age"));

    return "success";
}

 

  • Map 형태(key=value)로 조회가 가능하다

ex) http://localhost:8080/v6/request-param?name=sparta&age=100

  • MultiValueMap 형태(key=[value1, value2])로 조회가 가능하다.
@ResponseBody
@GetMapping("/v6/request-param")
public String requestParamV6(
        @RequestParam MultiValueMap<String, String> map
) {
    // logic
    log.info("name={}", map.get("name"));
    log.info("age={}", map.get("age"));

    return "success";
}
  • ex) http://localhost:8080/v6/request-param?
  • name=sparta&name=wonuk&name=tutor&age=100

💡 파라미터 Map의 Value가 1개인 경우에는 Map, 여러 개인 경우 MultiValueMap을 사용한다. 하지만 대부분의 파라미터 값은 한 개만 존재한다.

 

 

 

@ModelAttribute

📌 HTTP 요청 데이터(Form 데이터, 쿼리 파라미터 등)를 객체에 바인딩하거나, 뷰에 데이터를 전달하는 데 사용됩니다.

  • 요청 파라미터를 받아 필요한 Object로 바인딩 해준다. 주로 HTML 폼에서 전송된 데이터를 바인딩하고 HTTP Method POST인 경우 사용된다.
  • 매개변수에 사용: 요청 데이터를 객체로 매핑.
  • 메서드에 사용: 뷰에 공통 데이터를 추가.

 

주요 역할

1. 요청 데이터를 객체로 바인딩

  • 클라이언트에서 전달된 **요청 데이터(쿼리 파라미터, 폼 데이터)**를 자동으로 객체 필드에 바인딩합니다.
  • 주로 POST 요청의 폼 데이터를 처리할 때 사용됩니다.

2. 뷰(View)에 데이터 전달

  • 컨트롤러 메서드에서 반환 값이 아닌, 뷰에 데이터를 추가합니다.
  • 뷰에 공통적으로 전달해야 하는 데이터를 설정할 때 유용합니다.

 

 

특징 및 동작

1. 객체 생성 및 데이터 바인딩

  • Spring은 @ModelAttribute를 사용하면 다음 단계를 수행합니다:
    1. 지정된 클래스의 객체를 생성합니다.
    2. 요청 데이터(쿼리 파라미터, 폼 데이터)를 객체 필드에 매핑합니다.

2. 요청 방식에 따른 동작

  • GET 요청: 객체를 초기화하거나 뷰에 데이터를 전달.
  • POST 요청: 폼 데이터를 객체에 바인딩.

3. 생략 가능

  • @ModelAttribute는 생략 가능하며, 매개변수가 사용자 정의 객체라면 Spring이 자동으로 @ModelAttribute를 적용합니다.

 

주요 속성

  설명
value @ModelAttribute("name") 형식으로 모델 객체의 이름을 지정.
뷰에 데이터 전달 메서드에 사용하면 데이터를 뷰에 전달.
객체 바인딩 매개변수에 사용하면 요청 데이터를 객체에 자동 바인딩.

 

 

주의 사항

  1. 필드 이름 매칭:
    • 요청 데이터의 Key와 객체 필드 이름이 동일해야 바인딩됩니다.
    • Key와 필드 이름이 다르면 바인딩되지 않습니다.
  2. 유효성 검증:
    • @Valid와 함께 사용하여 요청 데이터를 검증할 수 있습니다
  3. 중복 데이터 처리:
    • 동일한 이름의 데이터를 여러 메서드에서 설정하면, 마지막으로 설정된 데이터가 우선합니다.
@PostMapping("/register")
public String registerUser(@Valid @ModelAttribute User user, BindingResult result) {
    if (result.hasErrors()) {
        return "form";
    }
    return "success";
}

 

 

기존 코드

@Data는 @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructer를 자동으로 설정해주는 역할을 한다. 테스트 용도로만 사용하고 실무에서는 잘 사용하지 않는다.

 

  • ex) http://localhost:8080/v1/tutor + x-www-form-urlencoded
@Data
public class Tutor {

	private String name;
	private int age;

}

@Controller
public class ModelAttributeController {

	@ResponseBody
  @PostMapping("/v1/tutor")
  public String requestParamV1(
          @RequestParam String name,
          @RequestParam int age
  ) {
      Tutor tutor = new Tutor();
      tutor.setName(name);
      tutor.setAge(age);

      return "tutor name = " + name + " age = " + age;
  }

}

 

  • @RequestParam 의 Mapping을 사용하게 되면 위와 같은 객체를 생성하는 코드가 포함된다.
    • @ModelAttribute 는 해당 과정을 자동화 한다.
  • Postman
POST /v1/tutor
content-type: application/x-www-form-urlencoded

name=wonuk&age=100

 

 

@ModelAttribute 적용

  • ex) http://localhost:8080/v2/tutor+ x-www-form-urlencoded
@ResponseBody
@PostMapping("/v2/tutor")
public String modelAttributeV2(
				@ModelAttribute Tutor tutor													
) {
		
	String name = tutor.getName();
	int age = tutor.getAge();

	return "tutor name = " + name + " age = " + age;
}
  • Postman
POST /v2/tutor
content-type: application/x-www-form-urlencoded

name=wonuk&age=100

 

  • @ModelAttirubte 동작 순서
    1. 파라미터에 @ModelAttribute가 있으면 파라미터인 Tutor 객체를 생성한다.
    2. 요청 파라미터 이름으로 객체 필드의 Setter를 호출해서 바인딩한다.
      1. 파라미터 이름이 name 이면 setName(value); 메서드를 호출한다.
      2. 파라미터 이름과 필드 이름이 반드시 같아야 한다!
  • @Data의 Setter가 없다면?
@Getter
public class Tutor {

	private String name;
	private int age;

}

  • 객체 필드에 값이 set되지 않는다.

 

  • 파라미터의 타입이 다른 경우
    • 만약 요청 파라미터 age 에 int가 아닌 String 이 전달된다면?
    ex) http://localhost:8080/v2/tutor+ x-www-form-urlencoded
POST /v2/tutor
content-type: application/x-www-form-urlencoded

name=wonuk&age=nbcamp

  • BindException 발생
  • 이런 경우 때문에 **Validation(검증)**이 필요하다.

 

@ModelAttirubte 생략

  • @ModelAttribute와 지난 시간에 배운 @RequestParam은 모두 생략이 가능하다.

ex) http://localhost:8080/v3/tutor+ x-www-form-urlencoded

POST /v3/tutor
content-type: application/x-www-form-urlencoded

name=wonuk&age=100
@ResponseBody
@PostMapping("/v3/tutor")
public String modelAttributeV3(Tutor tutor) {

	String name = tutor.getName();
	int age = tutor.getAge();

	return "tutor name = " + name + " age = " + age;
}
  • Spring에서는 @RequestParam이나 @ModelAttribute가 생략되면
    • String, int, Integer 와 같은 기본 타입은 @RequestParam과 Mapping한다. V4
@ResponseBody
@PostMapping("/v4/tutor")
public String requestParamV2(
        String name,
        int age
) {
    return "tutor name = " + name + " age = " + age;
}

 

  • 나머지 경우들(객체)은 모두 @ModelAttribute 와 Mapping한다. V3

 

 

@RequestParam vs @ModelAttribute 차이

 

주요 차이점

  @RequestParam @ModelAttribute
목적 특정 **단일 파라미터(쿼리 파라미터, 폼 데이터)**를 매핑. 요청 데이터를 객체 형태로 매핑.
대상 데이터 단일 Key-Value 쌍 (예: ?key=value) 여러 Key-Value 쌍을 객체의 필드에 매핑.
적용 대상 개별 매개변수. 객체.
데이터 출처 쿼리 파라미터, 폼 데이터. 폼 데이터, 쿼리 파라미터, 경로 변수 등.
기본값 설정 defaultValue 속성을 통해 가능. 객체 필드에 기본값을 설정하거나, 빈 객체 생성.
뷰 데이터 전달 뷰 데이터 전달 기능 없음. 뷰에 데이터를 전달할 수 있음 (메서드 레벨 사용).
생략 가능 여부 생략 불가능 (명시적으로 사용해야 함). 매개변수에서 생략 가능 (Spring이 자동으로 객체를 매핑).
주요 사용 사례 단순 쿼리 파라미터 또는 단일 데이터 처리. 폼 데이터나 복잡한 데이터 구조(객체) 처리.

 

사용 목적에 따른 선택

@RequestParam

  • 요청 데이터가 단순한 Key-Value 쌍이고, 각각 개별적으로 처리할 경우.
  • 쿼리 파라미터나 단일 폼 데이터 값에 적합.

@ModelAttribute

  • 요청 데이터가 객체로 표현 가능한 경우.
  • 복잡한 폼 데이터(여러 필드가 있는 객체)를 처리하거나, 뷰와 데이터를 쉽게 연동할 때 유리.

 

요약

상황 선택
단일 쿼리 파라미터 처리. @RequestParam
복잡한 폼 데이터를 객체로 매핑. @ModelAttribute
기본값 설정이 필요한 경우. @RequestParam
뷰에 데이터를 전달해야 하는 경우. @ModelAttribute
객체 기반 데이터 처리가 필요한 경우. @ModelAttribute

 

결론

  • **@RequestParam**은 단일 데이터(Key-Value) 처리에 적합하며, 간단한 요청에 사용됩니다.
  • **@ModelAttribute**는 폼 데이터나 요청 데이터를 객체 형태로 처리하거나, 뷰와 연동된 데이터를 전달할 때 유용합니다.
  • @RequestParam, @ModelAttribute는 GET + Query Parameter와, POST HTML Form Data를 바인딩하는 방법이다.

 

HTTP Message Body (요청)

📌 HTTP 요청(Request) 또는 응답(Response)에서 실제 데이터를 담는 부분입니다.

  • 주로 클라이언트와 서버 간에 전송되는 JSON, XML, HTML, 바이너리 파일, 텍스트 등 다양한 콘텐츠를 포함합니다.

 

특징

  • 이제부터 배울 내용은 HTTP Message Body에 직접적으로 Data가 전달되는 경우이다.
    • Request Body의 Data를 바인딩하는 방법이다.
  • REST API에서 주로 사용하는 방식이다.
  • HTTP Method POST, PUT, PATCH에서 주로 사용한다.
    • GET은 Request Body가 존재할 수는 있지만 권장하지 않는다.
  • JSON, XML, TEXT 등을 데이터 형식으로 사용한다.

 

 

HTTP Message 구조

 

 

HTTP Request, Response 예시

  • Server에서 Request로 전달받은 Data를 처리하기 위해서 바인딩 해야 한다.

ex) JSON → Object

 

 

 

TEXT

📌  HTTP Request Body에 Data가 전송되는 경우 HttpMessageConverter를 통해 바인딩된다.

  • 현대에는 Restful API를 주로 사용하고 있어서 대부분의 경우 JSON 형식으로 통신한다.

 

HttpServletRequest 예시

  • request.getInputStream();
@Slf4j
@Controller
public class RequestBodyStringController {
	
	@PostMapping("/v1/request-body-text")
  public void requestBodyTextV1(
          HttpServletRequest request,
          HttpServletResponse response
  ) throws IOException {

      ServletInputStream inputStream = request.getInputStream();
      String bodyText = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
      
      response.getWriter().write("response = " + bodyText);

  }

}
  • Request → Body → raw → Text

  • Request Header Content-Type : text/plain

 

I/O 예시

  • InputStream(읽기) 파라미터 지원
    • HTTP Request Body Data 직접 조회
  • OutputStream(쓰기) 파라미터 지원
    • HTTP Response Body 직접 결과 출력
@PostMapping("/v2/request-body-text")
public void requestBodyTextV2(
				InputStream inputStream,
				Writer responseWriter
) throws IOException { 
		
	String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
	
	responseWriter.write("response = " + bodyText);
}

Postman

HttpEntity 예시

  • HttpMessageConverter 사용
  • HttpEntity를 사용하면 HttpMessageConverter를 사용한다.
@PostMapping("/v3/request-body-text")
public HttpEntity<String> requestBodyTextV3(HttpEntity<String> httpEntity) { 
		
	// HttpMessageConverter가 동작해서 아래 코드가 동작하게됨
	String body = httpEntity.getBody();
		
	return new HttpEntity<>("response = " + body); // 매개변수 = Body Message
	
}

 

Postman

  • Spring의 HttpMessageConverter 덕분에 간편하게 Request Data에 접근할 수 있다.
    1. HttpEntity를 사용하면 HttpMessageConverter가 동작하여 자동으로 매핑된다.
    2. 요청 뿐만이 아닌 응답까지 HttpEntity 하나만으로 사용이 가능해진다.

 

 

HttpEntity

📌 HTTP 요청(Request)와 응답(Response)의 헤더(Header)와 본문(Body)을 캡슐화하는 데 사용됩니다.

  • HttpEntity는 HTTP Header, Body 정보를 편리하게 조회할 수 있도록 만들어준다.

 

주요 특징

  • 헤더(Header)와 바디(Body) 모두를 포함:
    • HTTP 요청 또는 응답의 헤더와 바디를 관리.
  • Immutable 객체:
    • HTTP 메시지를 불변(Immutable) 상태로 다룸.
  • Spring의 HTTP 클라이언트와 서버 모두에서 사용 가능:
    • REST API 호출에서 요청을 보낼 때, 응답 데이터를 받을 때 활용.

 

HttpEntity 역할

  1. Http Request Body Message를 직접 조회한다
  2. Request 뿐만 아니라 Response도 사용할 수 있도록 만들어준다.
  3. Response Header 또한 사용할 수 있다.
  4. Request Parameter를 조회하는 기능들과는 아무 관계가 없다.
  5. View를 반환하지 않는다.

 

주요 클래스

HttpEntity는 Spring에서 두 가지 주요 클래스의 기반이 됩니다:

  1. RequestEntity:
    • HTTP 요청을 표현.
    • 요청의 헤더, URL, HTTP 메서드 등을 캡슐화.
    • HTTP Request Method, URL 정보가 추가 되어있다.
  2. ResponseEntity:
    • HTTP 응답을 표현.
    • 응답의 상태 코드, 헤더, 바디를 관리.
    • HTTP Response 상태 코드 설정이 가능하다.
@Controller
public class RequestBodyStringController {
	
	@PostMapping("/v4/request-body-text")
  public HttpEntity<String> requestBodyTextV4(RequestEntity<String> httpEntity) {

      // HttpMessageConverter가 동작해서 아래 코드가 동작하게됨
      String body = httpEntity.getBody();
      // url, method 사용 가능

      return new ResponseEntity<>("response = " + body, HttpStatus.CREATED); // Body Data, 상태코드
  }

}

 

Postman

  • 위 방법을 적용해도 불편하다.
    • Data를 httpEntity에서 꺼내어 사용해야 한다.
Spring은 Http RequestBody Message를 읽어서 String이나 Object로 자동으로 변환해준다. 이때 HttpMessageConverter가 사용된다. 

 

 

@RequestBody, @ResponseBody

📌 Spring에서 @RequestBody, @ResponseBody 어노테이션을 사용하면 각각 Request, Response 객체의 Body에 편하게 접근하여 사용할 수 있다.

@Controller // @RestController = @Controller + @ResponseBody
public class RequestBodyStringController {
	
  @ResponseBody
  @PostMapping("/v5/request-body-text")
  public String requestBodyTextV5(
          @RequestBody String body,
          @RequestHeader HttpHeaders headers
  ) {
      // HttpMessageConverter가 동작해서 아래 코드가 동작하게됨
      String bodyMessage = body;

      return "request header = " + headers + " response body = " + bodyMessage;
  }
}

Postman

  • @RequestBody
    • 요청 메세지 Body Data를 쉽게 조회할 수 있다.
  • @RequestHeader
    • 요청 헤더 정보 조회
  • @ResponseBody
    • 응답 메세지 바디에 값을 쉽게 담아서 전달할 수 있도록 해준다.
    • View가 아닌 데이터를 반환한다.

 

요약

  1. 요청 파라미터, HTML Form Data에 접근하는 경우
    • @RequestParam, @ModelAttribute 를 사용한다.
  2. Http Message Body에 접근하는 경우
    • @RequestBody를 사용한다. (JSON, XML, TEXT)

 

 

HTTPMessageConverter

📌 HTTP 요청과 응답의 본문(Body)을 Java 객체와 직렬화 가능한 데이터(JSON, XML, 텍스트 등)로 변환하는 역할을 합니다.

  • Spring MVC는 클라이언트와 서버 간 데이터를 효율적으로 교환하기 위해 HTTPMessageConverter를 사용합니다.
  • MappingJackson2HttpMessageConverter는 JSON을 처리하는 대표적인 HTTPMessageConverter의 구현체이다.

 

HttpMessageConverter의 역할

  • 데이터를 Obejct로 변환한다. 대표적으로 JSON을 변환한다.

 

  1. 요청 본문 → Java 객체 변환 (@RequestBody 사용 시):
    • HTTP 요청의 JSON, XML, 텍스트 데이터를 Java 객체로 변환합니다.
  2. Java 객체 → 응답 본문 변환 (@ResponseBody 사용 시):
    • Java 객체를 JSON, XML, 텍스트 등으로 직렬화하여 클라이언트로 전송합니다.

 

 

  • @RequestBody
    • 요청 데이터 + Request Header를 참고하여 Object로 변환한다.
      • HTTP Request Body(JSON Data) → Converter(Jackson) → Object
      • Reqeust Header → Content-Type : application/json(전달할 데이터 형식)
  • @ResponseBody
    • 응답 데이터 + Accept Header를 참고하여 원하는 데이터 형식으로 변환한다.
      • Object → Converter(Jackson) → HTTP Response Body(JSON Data)
      • Request Header → Accept : application/json(허용할 데이터 형식)

 

동작 원리

Spring MVC는 요청과 응답 처리 시 다음 과정을 수행합니다:

  1. 요청 처리:
    • 클라이언트가 HTTP 요청 본문에 데이터를 포함시켜 보냅니다.
    • Spring은 HttpMessageConverter를 사용하여 요청 데이터를 Java 객체로 변환.
  2. 응답 처리:
    • 컨트롤러에서 반환한 Java 객체를 HttpMessageConverter가 JSON, XML 등으로 직렬화하여 응답 본문에 포함.

 

주요 HTTPMessageConverter 구현체

Spring은 다양한 데이터 형식에 따라 여러 HttpMessageConverter 구현체를 제공합니다:

구현체 데이터 형식 설명
MappingJackson2HttpMessageConverter JSON Jackson 라이브러리를 사용하여 JSON 변환 처리.
GsonHttpMessageConverter JSON Gson 라이브러리를 사용하여 JSON 변환 처리.
Jaxb2RootElementHttpMessageConverter XML JAXB를 사용하여 XML 변환 처리.
StringHttpMessageConverter Plain Text 문자열 데이터를 처리.
FormHttpMessageConverter Form Data (application/x-www-form-urlencoded) HTML 폼 데이터를 처리.
ByteArrayHttpMessageConverter Binary Data (byte[]) 바이너리 데이터를 처리.

 

장점

  1. 자동화:
    • 요청과 응답 데이터를 자동으로 변환하여 개발자의 부담을 줄임.
  2. 유연성:
    • JSON, XML, Plain Text 등 다양한 데이터 형식을 지원.
  3. 확장 가능성:
    • 사용자 정의 HttpMessageConverter로 맞춤형 데이터 처리 가능

 

요약

  • **HttpMessageConverter**는 HTTP 요청 및 응답의 본문 데이터를 처리하는 Spring의 핵심 컴포넌트입니다.
  • 요청 데이터를 Java 객체로 변환하거나, Java 객체를 JSON, XML 등으로 변환하여 클라이언트에 전달합니다.
  • Spring은 JSON 변환을 기본적으로 지원하며, 필요에 따라 사용자 정의 구현체를 추가할 수 있습니다.

'Back-End (Web) > Spring' 카테고리의 다른 글

[Spring] Server에서 Client로 Data를 전달하는 방법  (0) 2024.12.19
[Spring] HTTP Message Body & TEXT  (0) 2024.12.18
[Spring] Request Mapping  (1) 2024.12.16
[Spring] Spring Annotation  (1) 2024.12.15
[Spring] Spring MVC 패턴  (1) 2024.12.14

@RequestMapping

📌클라이언트의 요청 URL과 이를 처리할 컨트롤러의 클래스나 메서드를 매핑하는 데 사용됩니다.

  • 특정 URL로 Request를 보내면 들어온 요청을 Controller 내부의 특정 Method와 Mapping 하기 위해 사용한다.
  • Client로부터 요청이 왔을 때 어떤 Controller가 호출될지 Mapping하는것은 단순히 URL로 Mapping 하는것이 아니라 여러가지 요소(URL, Method 등)를 조합하여 Mapping한다.

 

@RequestMapping과 Handler Adapter의 차이

@RequestMapping Handler Adapter
HTTP 요청과 컨트롤러 메서드를 연결하는 어노테이션 DispatcherServlet과 핸들러(컨트롤러) 사이를 연결하는 구현체
메타데이터로써 동작 (어떤 요청이 어떤 메서드와 연결될지) 요청을 실행 가능한 형태로 변환하고 핸들러 실행.
개발자가 컨트롤러 메서드에 명시적으로 사용. Spring 내부에서 동작하며, 개발자가 직접 호출하지 않음.

 

주요 기능

  • 요청 URL과 매핑: 특정 URL 경로로 들어오는 HTTP 요청을 처리할 메서드나 클래스와 연결.
  • HTTP 메서드 지정: GET, POST, PUT, DELETE 등 특정 HTTP 메서드에만 반응하도록 설정.
  • 요청 조건 추가: 요청 헤더, 파라미터 등 특정 조건에 맞는 요청만 처리 가능.

 

요청 종류

  • @RequestMapping
    1. Spring Boot 3.0 버전 이하
      • URL path /example, /example**/** 모두 허용(Mapping)한다.
    2. Spring Boot 3.0 버전 이상(현재 버전)
      • URL path /example 만 허용(Mapping)한다.
    3. 속성값들을 설정할 때 배열 형태로 다중 설정이 가능하다
    ex) @RequestMapping**({**”/example”, “/example2”, “/example3”**})**
    1. HTTP Method POST, GET, PUT, PATCH, DELETE, HEAD 모두 허용한다
    2. method 속성으로 HTTP 메서드를 지정하면 지정된것만 허용한다.
package com.example.springbasicannotation.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

// 응답 데이터를 반환한다.
@RestController
public class RequestMappingController {

    // HTTP Method 는 GET만 허용한다.
    @RequestMapping(value = "/v1", method = RequestMethod.GET)
    public String exampleV1() {
        // logic
        return "this is sparta!";
    }

}

 

실행결과

 

  • 만약, 속성으로 설정된 HTTP Method로 요청이 오지 않는다면?
    • localhost:8080/v1 + POST, PUT, PATCH, DELETE 의 경우

  • HTTP 응답 상태코드 405(Client측 에러), Method Not Allowed Exception 반환

 

  • @GetMapping

  1. Target(ElementType.METHOD) Method Level에 해당 어노테이션을 적용한다 라는 의미
  2. 내부적으로 @RequestMapping(method = RequestMethod.GET) 을 사용하고 있다.

 

코드예시

// Post, GET, Put, Patch, Delete 모두 가능
@GetMapping(value = "/v2")
public String exampleV2() {
	// logic
	return "this is sparta!";
}
  • Spring이 제공하는 Annotation들의 내부에 다 선언되어 있다.
    • 대부분의 필요한 기능들이 이미 만들어져 있다. 사용 하면된다.
  • @RequestMapping 보다는 직관적이고 축약된 @GetMapping, @PostMapping 형식을 일반적으로 사용한다.
  • 실행 결과

 

 

  • @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping
    • 모두 위의 @GetMapping과 같은 구조를 가지고 있다.

 

 

 

 

 

  • @RequestMapping는 언제 사용하는가?

  • @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping의 Target은 Method Level 이다.
  • 반면에 @RequestMapping의 Target은 class, method 레벨에 적용이 가능하다.
  • Restful API의 계층 구조

메소드 레벨(Method Level)이란?

메소드 레벨클래스의 메서드에 적용할 수 있는 어노테이션의 사용 범위를 의미합니다.
즉, 메서드 레벨의 어노테이션은 클래스 내부의 특정 메서드에만 적용되어 동작합니다.

 

ex) users/{userId}, category/{categoryId}/product/{productId}

  • prefix로 선언할 URL을 class 레벨에 적용하는 것에 주로 사용된다.
@RequestMapping("/prefix")
@RestController
public class RequestMappingController {
	// Post, GET, Put, Patch, Delete 모두 가능
	@GetMapping(value = "/v3")
	public String exampleV3() {
		// logic
		return "this is sparta!";
	}

}

 

 

 

 

@PathVariable

📌 URL 경로에 포함된 변수를 메서드의 매개변수로 전달하기 위해 사용됩니다. 주로 RESTful 웹 서비스에서 동적인 요청 처리를 위해 사용됩니다.

  • HTTP 특성 중 하나인 비연결성을 극복하여 데이터를 전달하기 위한 방법 중 하나이다. URL로 전달된 값을 파라미터로 받아오는 역할을 수행한다.

 

동작 원리

  • URL 경로에 **변수(placeholder)**를 정의하고, 해당 값을 컨트롤러 메서드의 매개변수로 전달합니다.
  • @PathVariable은 요청 경로에서 변수 값을 추출하여 메서드 파라미터에 바인딩합니다.

 

  • @PathVariable
    1. 경로 변수를 중괄호에 둘러싸인 값으로 사용할 수 있다.
    ex) user/{id}
    1. 기본적으로 @PathVariable로 설정된 경로 변수는 반드시 값을 가져야 하며 값이 없으면 응답 상태코드 404 Not Found Error가 발생한다.
    2. 최근 Restful API를 설계하는 것이 API의 기준이 되며 해당 어노테이션의 사용 빈도가 높아졌다.
더보기

Restful API를 설계하게 되면 URL path 만으로 어떤 Resource을 사용하는지, HTTP Method 만으로 어떤 기능이 동작되는지 쉽게 알아볼 수 있다.

 

Restful API

https://restfulapi.net/resource-naming/

 

  • Create - POST
  • Read - GET
  • Update - PUT, PATCH
  • Delete - DELETE
  • Restful API 설계 예시
    • postId글의 comment 댓글 작성
      • POST + posts/{postId}/comments
    • postId글의 comment 댓글 전체 조회
      • GET + posts/{postId}/comments
    • postId글의 commentId 댓글 단 건 조회
      • GET + posts/{postId}/comments/{commentId}
    • postId글의 commentId 댓글 수정
      • PUT + posts/{postId}/comments/{commentId}
    • postId글의 commentId 댓글 삭제
      • DELETE + posts/{postId}/comments/{commentId}

 

  • @PathVariable 규칙
    1. 파라미터 변수명과 PathVariable 변수명이 같으면 속성 값 생략 가능
@RequestMapping("/posts")
@RestController
public class PathVariableController {
	
	// postId로 된 post 단건 조회
	@GetMapping("/{postId}")
	public String pathVariableV1(@PathVariable("postId") Long data) {
		// logic
		String result = "PathvariableV1 결과입니다 : " + data;
		return result;
	}
}

 

@RequestMapping("/posts")
@RestController
public class PathVariableController {
	
	// 변수명과 같다면 속성값 생략가능
	@GetMapping("/{postId}")
	public String pathVariableV2(@PathVariable Long postId) {
		// logic
		String result = "PathvariableV2 결과입니다 : " + postId;
		return result;
	}
	
}

 

 

 

2. @PathVariable 다중 사용 가능

 

@RestController
public class PathVariableController {
	
	@GetMapping("/{postId}/comments/{commentId}")
	public String pathVariableV3(
																@PathVariable Long postId,
																@PathVariable Long commentId
															) {
		// logic
		String result = "PathvariableV3 결과입니다 postId : " + postId + "commentsId : " + commentId;
		return result;
	}
	
}

@RequestMapping("/posts/{postId}")
@RestController
public class PathVariableController {
	
	@GetMapping("/comments/{commentId}")
	public String pathVariableV4(
																@PathVariable Long postId,
																@PathVariable Long commentId
															) {
		// logic
		String result = "PathvariableV4 결과입니다 postId : " + postId + "commentsId : " + commentId;
		return result;
	}
	
}

 

 

특정 파라미터 매핑

📌 속성 설정을 통하여 특정 헤더, 특정 파라미터와 Mapping 할 수 있다.

 

Parameter 추가 매핑

  • 특정 파라미터와 매핑하는 방법
package com.example.springbasicannotation.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ParameterController {

    // parms 속성값 추가
    @GetMapping(value = "/users", params = "gender=man")
    public String params() {
        // logic
        String result = "params API가 호출 되었습니다.";
        return result;
    }

}

 

  • 실제 URL GET http://localhost:8080/users**?gender=man** 파라미터가 있어야 호출된다.
  • 실행결과

  • 파라미터가 없다면?

400 Bad Request 클라이언트 측 에러

 

  • 속성 작성 규칙
    1. params = "gender"
      • params의 key값은 커스텀이 가능하다
      • value는 없어도 된다.
    2. params = "!gender"
      • gender가 없어야 한다.
    3. params = "gender=man"
      • gender=man 이어야 한다.
    4. params = "gender!=man"
      • params의 value값이 man가 아니여야 한다.
    5. params = {"gender=man", "gender=woman"}
      • 배열로 속성 값을 여러 개 설정이 가능하다.

 

특정 Header 매핑

  • 특정 Header와 매핑하는 방법
@RestController
public class ParameterController {
	
	// headers 속성값 추가
  @PostMapping(value = "/users", headers = "Content-Type=application/json")
  public String headers() {
      // logic
      String result = "headers API가 호출 되었습니다.";
      return result;
  }
	
}
  • Postman → Body → raw → JSON

 

  • Postman → Headers → hidden

 

 

  • HTTP Header를 사용하기 때문에 Postman으로 테스트 해야 한다.

ex) key=Content-Type / value=application/json

  • 실행결과

  • 속성 작성 규칙은 위 params 속성 값의 규칙과 같다.

 

MediaType 매핑, consume(수용)

  • HTTP Header Content-Type(요청)과 매핑 된다.
@RestController
public class ParameterController {
	
	// consumes 속성값 추가
  @PostMapping(value = "/users", consumes = "application/json") // MediaType.APPLICATION_JSON_VALUE
  public String consumes() {
      // logic
      String result = "consumes API가 호출 되었습니다.";
      return result;
  }
	
}

 

  • consumes 속성 value값으로는 이미 Spring에서 제공되는 Enum인
  • MediaType.APPLICATION_JSON_VALUE 형태로 사용한다.

 

  • Postman → Body → raw → JSON

  • Postman → Body → raw → JSON

 

  • 파라미터가 없거나 다르다면?

  • HTTP 상태코드 405 Unsupported Media Type Exception 발생
  • 속성 작성 방법
    1. consumes=”application/json”
      • application/json 미디어 타입 허용
    2. consumes=”!application/json”
      • application/json 제외 미디어 타입 허용
    3. consumes=”application/*”
      • application/ 으로 시작하는 모든 미디어 타입 허용
    4. consumes=”*\\/*”
      • 모두 허용

 

MediaType 매핑 produces(제공)

  • 요청 헤더의 Accept 값에 따라서 produces 하는 값이 변한다.
@RestController
public class ParameterController {
	
	// produces 속성값 추가
  @GetMapping(value = "/users", produces = "text/plain")
  public String produces() {
      // logic
      String result = "text/plain 데이터 응답";
      return result;
  }
	
}
  • HTTP 요청 Accept Header에 Media Type이 있어야한다.

  • **/** : 전체 Media Type 허용
  • 실행결과

 

  • consumes 속성 사용법과 같다.
  • 위에 나온 모든 MediaType은 Spring이 제공하는 Enum을 사용하면 된다.

ex) produces = “application.json" → produces = MediaType.APPLICATION_JSON_VALUE

 

 

Spring이 지원하는 Parameter

📌 어노테이션 기반 Spring의 Controller는 다양한 파라미터를 쉽게 사용할 수 있도록 지원한다.

 

  • HTTP 헤더 조회
    • Spring에서 요청 Header에 쉽게 접근할 수 있다.
      • HttpServletRequest와 같이 파라미터로 다룰 수 있다.
    • Controller 예시
// 로깅
@Slf4j
@RestController
public class RequestHeaderController {

    @GetMapping("/request/headers")
    public String headers(
            HttpServletRequest request, // Servlet에서 사용한것과 같음
            HttpServletResponse response, // Servlet에서 사용한것과 같음
            @RequestHeader MultiValueMap<String, String> headerMap,
            @RequestHeader("host") String host,
            @CookieValue(value = "cookie", required = false) String cookie,
            HttpMethod httpMethod,
            Locale locale
    ) {
		    // Servlet
        log.info("request={}", request);
        log.info("response={}", response);
        
        // @RequestHeader
        log.info("headerMap={}", headerMap);
        log.info("host={}", host);
        
        // @CookieValue
        log.info("cookie={}", cookie);
        
        // HttpMethod
        log.info("httpMethod={}", httpMethod);
        
        // Locale
        log.info("Locale={}", locale);

        return "success";
    }
}

 

  • Postman API 호출

 

 

 

Log 출력 결과

  1. request
    • HttpServletRequest 객체 주소 값
  2. response
    • HttpServletRequest 객체 주소 값
  3. headerMap :
hashMap={
	user-agent=[PostmanRuntime/7.35.0], 
	accept=[*/*], 
	postman-token=[5f324c1c-7902-4750-9e01-2c4d093e8ad6],
	host=[localhost:8080],
	accept-encoding=[gzip, deflate, br],
	connection=[keep-alive]
}

 

  1. host
    • host 정보
  2. cookie
    • Header의 Cookie 값
  3. httpMethod
    • 호출에 사용한 HttpMethod
  4. Locale
    • 위치 정보를 나타내는 헤더
    • 우선순위가 존재한다.

 

 

MultiValueMap

Map과 유사하게 Key, Value 형식으로 구현되어 있지만 하나의 Key가 여러 Value를 가질 수 있다 HTTP Header, Reqeust Parameter와 같이 하나의 Key에 여러 값을 받을 때 사용한다.

ex )  key1=value1&key1=value2

MultiValueMap<String, String> linkedMultiValuemap = new LinkedMultiValueMap();

// key1에 value1 저장
linkedMultiValuemap.add("key1", "value1");
// key1에 value2 저장
linkedMultiValuemap.add("key1", "value2");

// key1에 저장된 모든 value get
List<String> values = linkedMultiValuemap.get("key1");

 

'Back-End (Web) > Spring' 카테고리의 다른 글

[Spring] HTTP Message Body & TEXT  (0) 2024.12.18
[Spring] @RequestParam & @ModelAttribute  (0) 2024.12.17
[Spring] Spring Annotation  (1) 2024.12.15
[Spring] Spring MVC 패턴  (1) 2024.12.14
[Spring] Spring Boot  (0) 2024.12.10

Lombok

📌 Java의 보일러플레이트 코드(반복적이고 지루한 코드)를 줄여주는 라이브러리입니다.

  • 간단한 어노테이션으로 getter, setter, equals, hashCode, toString, logger 생성 등을 자동으로 생성하여 코드의 간결성을 높이고 가독성을 향상시킵니다.
import lombok.ToString;

@ToString
public class Example {
    private String name;
    private int age;
}


=

@Override
public String toString() {
    return "Example(name=" + name + ", age=" + age + ")";
}

 

Lombok의 장점

  1. 코드 간소화:
    • getter, setter, equals, toString 등 반복적인 코드를 줄여줍니다.
  2. 가독성 향상:
    • 핵심 로직에만 집중할 수 있어 코드가 더 읽기 쉬워집니다.
  3. 유지보수성 개선:
    • 코드 중복이 줄어들어 수정할 곳이 줄어듭니다.

 

Lombok 사용 시 주의사항

  1. 의존성 문제:
    • Lombok은 컴파일 시점에 코드를 생성하므로, IDE와 빌드 환경이 Lombok을 지원해야 합니다.
  2. 타인의 코드 리뷰:
    • Lombok을 사용하면 실제 코드가 IDE에서 보이지 않아, 팀원이 Lombok을 모른다면 코드 리뷰가 어려울 수 있습니다.
  3. Java 표준 아님:
    • Lombok은 Java 표준 라이브러리가 아니기 때문에, 프로젝트 환경에 따라 사용을 제한할 수 있습니다.

 

 

@Slf4j

📌 Lombok에서 제공하는 어노테이션으로, SLF4J(Simple Logging Facade for Java) 기반의 로깅(Logger) 객체를 자동으로 생성합니다.

  • 로깅은 애플리케이션 개발에서 로그 메시지를 기록하여 디버깅, 모니터링, 운영 상태를 파악하는 데 매우 중요한 도구입니다.
  • Slf4j는 인터페이스이고 그 구현체로 Logback같은 라이브러리를 선택한다. 실제 개발에서는 Spring Boot가 기본으로 제공하는 Logback을 대부분 사용한다. (Slf4j는 껍데기 실제 로깅은 구현체가 한다.)
  • 헷갈릴까봐 기술하면, Logback이 Slf4j의 구현체 중 하나로, Spring Boot에서 기본 로깅 프레임워크로 사용된다.

 

Logging

  • Thread 정보, 클래스 이름과 같은 부가 정보를 함께 확인할 수 있다.
  • 실제 운영 환경에서는 System.out.println();을 사용하여 Console에 정보를 출력하지 않고, 별도의 로깅 라이브러리를 사용하여 로그를 출력한다.
  • Log Level 설정을 통하여 Error 메세지만 출력하도록 하도록 하기도 하고 로그 메세지를 일자별로 모아서 저장하여 외부 저장소에 보관하기도 한다.
    • Log Level
      • TRACE > DEBUG > INFO > WARN > ERROR
  • 이게 왜 있나 싶을 수 있는데, 서버는 출력을 터미널에 띄울 수 없다보니 따로 서버내에서 기록하게 한다. 실제로 개발자 환경에 console에서 그 결과를 확인할 수 있다. 더 나아가 오류를 미리 기록하여 모니터링을 돕는다. 서버에 적재하지 않는 프로그램이면 이렇게 하지 않겠지만 서버는 24시간 돌아가니 사람이 24시간 감시할거 아니면 저런 장치가 필요하다.

 

사용시 주의점

package com.example.springbasicannotation.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
public class Slf4jController {

    @RequestMapping("/logging")
    public String logging() {

        String sparta = "Sparta";
        // TRACE -> DEBUG -> INFO -> WARN -> ERROR
        log.trace("문자 trace={}", sparta);
        log.debug("문자 debug={}", sparta);

        // default
        log.info("문자 info={}", sparta);// 문자 연산을 진행하지 않는다.
        log.warn("문자 warn={}", sparta);
        log.error("문자 error={}", sparta);

        log.info("문자 info " + sparta); // 문자 연산을 먼저 해버린다.
        return "success";
    }

}

# com.example.springbasicannotation 하위 경로들의 로그 레벨을 설정한다.
logging.level.com.example.springbasicannotation=TRACE

 

  • Postman 호출

Default Level(INFO) API 호출

 

출력결과

 

level=TRACE

 

 

 

@Controller VS @RestController

📌 Annotation 기반의 Spring에서 Controller(Handler)를 만들 때 사용하는 어노테이션

  • 둘이 기능은 같지만, 데이터 반환 방식응답 처리 방식이 다르다.

 

주요 차이점

특징 @Controller @RestController
주요 용도 페이지(View) 기반 웹 애플리케이션 RESTful API 개발
기본 반환 타입 View 이름(HTML, JSP 등) JSON, XML 등 데이터
View Resolver View Resolver를 통해 뷰 파일로 매핑 사용하지 않음
응답 내용 HTML 페이지를 렌더링하여 반환 데이터 자체를 HTTP 응답으로 반환
애너테이션 결합 단일 어노테이션 @Controller + @ResponseBody

 

언제 사용해야 할까?

  • @Controller:
    • HTML, JSP 같은 페이지 기반 웹 애플리케이션 개발.
    • 템플릿 엔진(Thymeleaf, JSP)과 함께 사용.
  • @RestController:
    • JSON, XML 데이터를 반환하는 RESTful API 개발.
    • 클라이언트(브라우저, 앱 등)가 데이터를 요청하고 처리하는 백엔드 서비스 개발.

 

@Controller

📌MVC(Model-View-Controller) 아키텍처에서 사용되는 컨트롤러.

  • 클라이언트 요청을 처리하고, **View(HTML, JSP 등)**를 반환하는 데 사용.
    • View가 있는 경우에 사용한다.
  • 주로 페이지 기반의 웹 애플리케이션 개발에 적합.
  • 즉, Template Engine인 Thymeleaf, JSP 등을 사용하는 경우

특징

  1. View Resolver와 함께 사용:
    • @Controller는 뷰 이름을 반환하며, Spring의 View Resolver가 이 이름을 실제 뷰 파일 경로로 변환.
    • 예: return "home"; → /WEB-INF/views/home.jsp
  2. 주요 반환 타입:
    • 문자열(String): 뷰 이름.
    • ModelAndView: 데이터와 뷰 정보를 함께 반환.

 

@Controller
public class WebController {

    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", "Hello, World!");
        return "hello"; // 뷰 이름 반환 (예: hello.jsp)
    }
}

응답 흐름

  1. 클라이언트가 /hello로 요청.
  2. hello() 메서드가 실행되고, 뷰 이름 "hello" 반환.
  3. View Resolver가 "hello"를 실제 뷰 파일 경로(/WEB-INF/views/hello.jsp)로 매핑.
  4. 해당 JSP 파일이 렌더링되어 클라이언트에게 HTML 응답 반환.

 

  • @Target(ElementType.Type)
    • Class, Interface, Annotation, Enum, Record Declaration(Java16) 에 적용할 수 있다.
  • @Retention(RetentionPolicy.RUNTIME)
    • 클래스 파일(.class)에 저장되고, JVM에 의해 런타임 시점에 읽을 수 있다.
  • @Document
    • Javadoc 등의 문서화 도구에 의해 문서화되어야 함을 나타낸다.
  • @Component
    • Spring Bean에 등록한다.
    • 싱글톤으로 관리된다.
더보기


Thymeleaf 예시

  • SpringBoot build.gradle 의존성 추

 

  • main/resources/templates 가 기본 경로로 설정된다.
  • resources/templates/sparta.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
<h2>Thymeleaf Template Sample</h2>
</body>
</html>
  • 동작 순서
  • return 값이 String이면 ThymeleafViewResolver 에 의해 View Name으로 인식된다.

 

  • 호출결과
    • http://localhost:8080/view

 

 

 

 

@RestController

📌 RESTful 웹 서비스를 개발하기 위해 사용되는 컨트롤러.

  • 응답할 Data가 있는 경우에 사용한다.
  • 클라이언트 요청을 처리하고, JSON, XML 등 데이터 포맷으로 응답.
  • 페이지 대신 **데이터(API)**를 반환하는 데 사용.
  • 현재는 대부분 @RestController를 사용하여 API가 만들어진다. (Restful API)
  • return 값으로 View를 찾는것이 아니라 HTTP Message Body에 Data를 입력한다.

 

특징

  1. 데이터 반환에 특화:
    • 메서드의 반환값은 **자동으로 HTTP 응답 본문(body)**에 직렬화되어 클라이언트에 전달.
    • JSON이 기본 반환 포맷(SPRING BOOT에서는 기본적으로 Jackson 라이브러리를 사용).
  2. 주요 반환 타입:
    • 객체 또는 컬렉션: JSON으로 변환.
    • 문자열(String): 텍스트 응답.
  3. @Controller + @ResponseBody의 결합:
    • @RestController는 내부적으로 @Controller와 @ResponseBody를 합친 것과 같음.

 

코드 예제

@RestController
public class ApiController {

    @GetMapping("/api/hello")
    public Map<String, String> hello() {
        Map<String, String> response = new HashMap<>();
        response.put("message", "Hello, World!");
        return response; // JSON으로 변환되어 반환
    }
}

응답 흐름

  1. 클라이언트가 /api/hello로 요청.
  2. hello() 메서드가 실행되고, Map<String, String> 객체 반환.
  3. Spring Boot가 반환된 객체를 JSON으로 변환.
  4. JSON 응답을 클라이언트로 전달.

 

  • @Controller에 @ResponseBody가 결합된 어노테이션
  • @RestController는 @Controller와 달리 각 메서드마다 @ResponseBody를 추가하지 않아도 된다

 

 

 

더보기

 

 

호출 결과

 

 

결론

  • @Controller는 View를 반환하는 데 초점을 맞추고 있고, @RestController는 데이터(API 응답)를 반환하는 데 사용됩니다.
  • 개발 목적에 따라 적절한 어노테이션을 선택하여 사용하면 됩니다.
    예를 들어:
    • HTML 페이지 제공: @Controller + View Resolver.
    • REST API 제공: @RestController.

 

 

Spring  Annotation 자세히 보기

 

@Component

  • Spring Bean에 등록하는 역할을 수행한다.
    • Spring Bean은 애플리케이션의 구성 요소를 정의하는 객체이다.
    • WAS가 Servlet 코드를 읽어 컨테이너에 등록했던 것을 떠올려 봅시다.
    • Servlet Container(네트워크 기초 자료)
더보기
  • Servlet Container(네트워크 기초 자료)
  • 📚 Servlet을 지원하는 WAS 내부에는 서블릿 컨테이너가 있다. 서블릿 컨테이너는 서블릿을 초기화, 생성, 관리, 호출, 종료하는 역할을 수행한다.  
    • Servlet의 생명주기
      • Servlet은 서블릿 컨테이너가 생성 및 관리한다.
      • 즉, WAS(서블릿 컨테이너 포함)가 종료될 때 Servlet도 함께 종료된다.
    • Servlet 객체 생성시점
      • 개발자가 직접 인스턴스화 하여 사용하는것이 아닌, 코드만 작성하면 서블릿 컨테이너가 생성한다.
       

 

서블릿 예시 코드

@WebServlet(name="ExampleServlet", urlPatterns = "/example")
public class ExampleServlet extends HttpServlet { // HttpServlet을 상속받아 구현한다.
	
	@Override
	protected void service(
		HttpServletRequest request,  // HTTP 요청 정보를 쉽게 사용할 수 있게 만드는 Servlet
		HttpServletResponse response // HTTP 응답 정보를 쉽게 제공할 수 있게 만드는 Servlet
	) {
		// application logic
	}

}
@WebServlet(name="Example2Servlet", urlPatterns = "/example2")
// 위와 같은 코드

@WebServlet(name="Example3Servlet", urlPatterns = "/example3")
// 위와 같은 코드

@WebServlet(name="Example4Servlet", urlPatterns = "/example4")
// 위와 같은 코드

 

  • Servlet Container가 하는 일
    1. 서블릿을 초기화, 생성, 관리, 호출, 종료하는 역할을 수행
      1. Servlet 객체를 싱글톤으로 관리한다.
    2. 동시 요청에 대한 처리를 위해 Multi Thread를 지원한다.
💬 Q. 싱글톤이 무엇인가요?

A. 싱글톤은 객체를 하나만 생성하여 생성된 인스턴스를 공유하여 사용하는것을 의미합니다. 특정 클래스의 인스턴스가 여러개 생성되지 않도록 하여 자원의 낭비를 방지하고, 인스턴스를 공유함으로써 상태를 일관되게 유지하기 위함입니다. 하지만, 공유 변수 사용을 주의해야 합니다.

  • @Indexed
    • 클래스가 컴포넌트 스캔의 대상으로 Spring Bean에 더 빠르게 등록되도록 도와준다.
  • Spring Bean, 컴포넌트 스캔에 대해서는 숙련주차 강의에서 자세히 다룰 예정

 

@Target

  • @Target 이 선언된 하위 어노테이션이 어떤 범위에 적용되는지 설정한다.
  • ElementType Enum 속성

  • 각각의 Enum마다 적용되는 범위가 상단에 주석으로 설명되어 있다.

 

@Retention

  • @Retention 하위의 어노테이션이 얼마나 오래 유지되는지를 결정한다.
  • RetentionPolicy Enum 속성

  1. SOURCE
    • 소스 코드(.java)에서만 유지된다.
    • 컴파일러에 의해 클래스 파일로 저장되지 않는다.
  2. CLASS
    • 컴파일된 클래스 파일(.class)에 저장되지만, JVM이 실행 시 읽지 않는다. (주석과 같음)
    • Default 값이다.
  3. RUNTIME
    • 클래스 파일(.class)에 저장되고, JVM에 의해 런타임 시점에 읽을 수 있다.
    • 즉, 실제 런타임 시점의 코드에 반영되어 영향을 준다.

 

@Documented

  • Javadoc 등의 문서화 도구에 의해 문서화되어야 함을 나타낸다.

'Back-End (Web) > Spring' 카테고리의 다른 글

[Spring] @RequestParam & @ModelAttribute  (0) 2024.12.17
[Spring] Request Mapping  (1) 2024.12.16
[Spring] Spring MVC 패턴  (1) 2024.12.14
[Spring] Spring Boot  (0) 2024.12.10
[Spring] Spring Framework  (2) 2024.12.09

Spring MVC 구조

📌 Spring은 MVC 패턴에 프론트 컨트롤러 패턴, 어댑터 패턴이 적용된 구조를 가지고 있다.

  • MVC는 소프트웨어 설계 패턴으로 구축 개념에 가깝다. 당연히 구축 방식은 때에 따라 달라왔고 이를 Spring에서는 통합하여 하나의 템플릿으로 제공한다.

 

MVC 패턴 구조

  1. 요청이 오면 Controller에서 파라미터 정보 확인하여 비지니스 로직을 실행한다.
  2. 비지니스 로직의 결과 Data 를 Model에 담아서 View에 전달해준다.
  3. View는 모델의 Data를 참조하여 화면을 그려준다.

Spring MVC 구조

  • DispatcherServlet : Spring의 프론트(프론트엔드 아님, HTTP 요청의 최전선을 프론트라인이라 함) 컨트롤러
  • View : 인터페이스로 구성되어 있다, 확장성을 가지고 있다.

 

실행순서

  1. Client로 부터 HTTP 요청(Request)을 받는다.
  2. Handler 조회
    • Handler Mapping을 통해 요청 URL에 Mapping된 Handler(Controller)를 조회
  3. Handler를 처리할 Adapter 조회
    • Handler를 처리할 수 있는 Handler Adapter를 조회
  4. Handler Adapter 실행(handle)
    • 알맞은 ****어댑터가 존재한다면 ****Handler Adapter에게 요청을 위임한다.
  5. Handler 실행(호출)
    • Handler Adapter가 실제 Handler(Controller)를 호출하여 실행 및 결과 반환
  6. Model And View 반환(return)
    • Handler Adapter는 Handler가 반환 하는 정보를 ModelAndView 객체로 변환하여 반환
  7. viewResolver 호출(알맞은 View 요청)
    • View Resolver를 찾고 실행
  8. View 반환
    • View Resolver는 View의 논리 이름을 물리 이름으로 전환하는 역할을 수행하고 Rendering 역할을 담당하는 View 객체를 반환
  9. View Rendering
    • View를 통해서 View를 Rendering
더보기

1. Client로부터 HTTP 요청(Request)을 받는다

  • 고객(클라이언트)이 배달 앱에서 음식을 주문합니다. (예: "치킨 1마리 배달 요청")
  • 배달 앱이 이 주문을 **중앙 주문 처리 서버(SPRING MVC)**로 보냅니다.

2. Handler 조회 (Handler Mapping)

  • 서버는 고객이 요청한 **주문 정보(예: 치킨 배달)**를 확인하고, 이 요청을 처리할 적절한 음식점(Handler)을 찾아야 합니다.
  • 여기서 Handler Mapping 주문에 맞는 음식점을 조회하는 과정입니다.
    • 예: 고객이 "치킨"을 주문했으니, 근처의 치킨집을 선택합니다.

3. Handler를 처리할 Adapter 조회

  • 음식점마다 주문을 처리하는 방식(프로세스)이 다를 수 있습니다.
    • 어떤 음식점은 앱을 통해 바로 확인하고,
    • 어떤 음식점은 전화로 주문을 확인해야 합니다.
  • 이 단계에서는 요청을 처리할 수 있는 적절한 **Handler Adapter(음식점과 서버를 연결하는 도구)**를 찾습니다.
    • 예: 치킨집은 앱으로 주문을 확인하는 시스템이 있으니, 앱 기반 주문 어댑터를 선택.

4. Handler Adapter 실행(handle)

  • 적절한 **Handler Adapter(주문 어댑터)**가 음식점에 주문을 전달합니다.
    • 예: 앱이 "치킨 한 마리 주문 요청"을 음식점의 주문 시스템에 보냅니다.

5. Handler 실행(호출)

  • 음식점(Handler)이 실제로 주문을 준비하기 시작합니다.
    • 예: 주방에서 치킨을 튀기고 포장합니다.

6. Model And View 반환(return)

  • 음식점은 준비가 끝난 뒤, "주문이 완료되었습니다!"라는 메시지와 함께 준비된 치킨 데이터를 서버에 보냅니다.
    • 예: "치킨 1마리, 포장 완료!"
    • 여기서 ModelAndView는 이 데이터("치킨 데이터")와 응답 메시지를 함께 묶어서 반환하는 역할을 합니다.

7. View Resolver 호출 (알맞은 View 요청)

  • 서버는 고객에게 응답을 보내기 전에, 결과 데이터를 어떻게 보여줄지 결정합니다.
    • 예: 고객이 앱을 통해 결과를 볼 수 있어야 하므로, 앱 화면(View)을 설정해야 합니다.
  • View Resolver는 "치킨 준비 완료"라는 정보를 앱의 알맞은 화면(예: 주문 완료 페이지)으로 연결합니다.

8. View 반환

  • View Resolver는 "주문 완료 화면"을 앱에 전달합니다.
    • 예: 고객의 앱에 "주문 완료! 배달 중!"이라는 화면이 나타남.

9. View Rendering

  • 고객의 앱에서 실제로 화면이 표시됩니다.
    • 예: 고객은 "주문이 완료되었고, 곧 배달됩니다!"라는 메시지를 화면에서 확인합니다.

결론: 배달 서비스로 비유한 실행 흐름

  1. 고객이 배달 앱에 주문 요청을 보냄 (HTTP 요청)
  2. 서버가 요청을 처리할 음식점을 찾음 (Handler Mapping)
  3. 주문을 전달하는 적절한 방식을 선택 (Handler Adapter 조회)
  4. 음식점이 주문을 준비함 (Handler 실행)
  5. 음식점이 준비 상태를 서버에 반환 (ModelAndView 반환)
  6. 서버가 결과를 앱 화면에 맞게 변환 (View Resolver 호출)
  7. 결과가 앱 화면에 표시됨 (View Rendering)

 

요약

  • DispatcherServlet ( 요청과 응답의 중앙 처리국 )
    1. 클라이언트 HTTP Request를 알맞게 파싱하고 클라이언트에게 알맞은 응답을 반환
    2. 핸들러 목록 정보를 알고있다.
    3. 핸들러 어댑터 목록 정보를 알고있다.
  • HandlerAdapter (부서 담당 연결원)
    1. 자신이 처리할 수 있는 Handler인지 확인할 수 있는 기능(Method)이 필요하다.
    2. 프론트 컨트롤러에서 요청을 위임받았을 때 핸들러에게 요청을 지시하는 기능이 필요하다.
    3. return 시 Handler로부터 전달받은 결과를 알맞은 응답으로 변환한다.
  • Handler (처리자)
    1. 요청에 대한 로직을 수행하는 기능이 필요하다.

 

 

Dispatcher Servlet

📌 모든 HTTP 요청의 진입점으로 작동하는 프론트 컨트롤러(Front Controller)로, 요청 처리의 중앙 허브(총괄), 핸들러 매핑(배정), 핸들러 어댑터 실행(인력 파견), 응답 반환(우편전달)을 다 한다.

  • Spring MVC의 프론트 컨트롤러는 Dispatcher Servlet(Servlet의 한 종류)이다.

  1. Dispatcher Servlet은 HttpServlet을 상속 받아서 사용하고 Servlet의 한 종류이다.
  2. Spring Boot는 Dispatcher Servlet을 서블릿으로 자동으로 등록(내장 Tomcat WAS를 실행하면서 등록한다)하고 모든 URL 경로에 대해서 Dispatcher Servlet을 Mapping 한다. → (urlPatterns=”/”) = 요청이 들어오는 모든 url에 총괄 관리자를 붙인다.
  3. 더 자세한 URL 경로가 높은 우선순위를 가진다. = 요구사항이 자세한 손님부터 처리해 주겠다는 것 = 효율적이라서
    • 개발자가 만들 Servlet이 항상 우선순위가 높아서 실행된다.

 

DispatcherServlet의 service()

  1. Servlet이 호출되면 HttpServlet이 제공하는 service()가 호출된다.
  2. Spring MVC는 DispatcherServlet의 부모인 FrameworkServlet에서 service()를 Override 해두었다.
  3. FrameworkServlet.service()를 시작으로 여러 메서드가 호출됨과 동시에 가장 중요한DispatcherServlet.doDispatch()가 호출된다.

protected void doDispatch() {
...
// 1. 핸들러 조회
	mappedHandler = getHandler(processedRequest); 
	if (mappedHandler == null) {
		noHandlerFound(processedRequest, response); // NotFound 404
	}

// 2. 핸들러 어댑터 조회 : 핸들러를 처리할 수 있는 어댑터
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// 3. 핸들러 어댑터 실행
// 4. 핸들러 어댑터를 통해 핸들러 실행 
// 5. ModelAndView 반환 
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 여기 안에서 render   
	processDispatchResult(processedRequest, response, mappedHandler, mv,dispatchException);
	...
}


// processDispatchResult()
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		if (mv != null && !mv.wasCleared()) {
			// View Render 호출
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
}

// render()
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
	View view;
	String viewName = mv.getViewName();

// 6. ViewResolver를 통해 View 조회
// 7. View 반환
	view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

// 8. View Rendering
	view.render(mv.getModelInternal(), request, response);
}

 

 

 

Spring MVC의 주요 Interface

📌 Spring MVC는 DispatcherServlet 코드의 변경 없이 기능변경 및 확장이 가능하다. 기능들이 대부분 Interface로 만들어져 있기 때문이다.

  • 이쯤에서 한번 더 짚고 넘어가면 왜 대부분이 클래스가 아니라 인터페이스로 구현되어 있냐는 질문엔, 다형을 위해서가 정답이다.
  • org.springframework.web.servlet
    1. HandlerMapping
    2. HandlerAdapter
    3. ViewResolver
    4. View
  • 당연히 이 모든 인터페이스를 알 필요는 없다. 하지만 자주 사용하는 인터페이스에 대해서는 잘 알아야 확장이나 구현, 문제 해결에 있어 용이하다.

 

Controller Interface

📌Spring 2.5 이전에 모든 컨트롤러가 구현해야 했던 표준 인터페이스입니다. 이 인터페이스는 클라이언트의 요청을 처리하고, 결과를 View로 전달하는 기본적인 방법을 정의합니다.

  • Controller Interface를 implements 하여 구현하게되면 개발자가 원하는 Controller(Handler)를 사용할 수 있게됩니다.
  • 추후 강의에 등장할 현대에 사용하는 Annotation 기반 Spring의 @Controller와는 역할이 비슷하지만 연관은 없습니다.
package com.example.springbasicmvc.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

// Spring Bean 이름을 URL로 설정
@Component("/example-controller")
public class ExampleController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("example-controller가 호출 되었습니다.");
        return null;
    }
}
  • http://localhost:8080/example-controller 로 HTTP 요청을 하게 되면 응답결과가 반환된다.

  • 출력 결과

 

  • @Component
    • Spring Bean에 등록하는 역할을 수행한다.
      • Spring Bean은 애플리케이션의 구성 요소를 정의하는 객체이다.
      • 마치 Servlet이 Servlet Container에 등록되는 것과 같다.

 

 

  1. Handler Mapping
    1. 핸들러 매핑에서 ExampleController( Spring 애플리케이션에서 특정 요청을 처리하는 컨트롤러 클래스)를 찾을 수 있어야 한다.
    → Spring Bean의 이름으로 핸들러를 찾을 수 있는 핸들러 매핑이 필요하다.
  2. Handler Adapter
    1. Handler Mapping을 통해 찾은 핸들러를 실행할 수 있는 Handler Adapter가 필요
    → Controller Interface를 실행할 수 있는 Handler Adapter를 찾고 실행한다.
  • 놀랍게도 Handler Mapping은 찾기만 하고 Handler Adapter가 Handler에게 배치해줘야 한다.
  • 유지보수와 확장성에 용이하다...

 

Spring Boot의 Handler Mapping, Handler Adapter

📌 Spring Boot를 사용하면 개발에 필요하여 자동으로 등록되는 HandlerMapping과 HandlerAdapter들이 있다.

  • HandlerMapping, HandlerAdapter 모두 우선순위대로 조회한다.

 

  • HandlerMapping
    • 우선순위 순서
    1. RequestMappingHandlerMapping
      • 우선순위가 가장 높다
      • Annotation 기반 Controller의 @RequestMapping에 사용
    2. BeanNameUrlHandlerMapping(위 예시코드에 사용)
      • Spring Bean Name으로 HandlerMapping
  • HandlerAdapter
    • 우선순위 순서
    1. RequestMappingHandlerAdapter
      • Annotation 기반 Controller의 @RequestMapping에서 사용
    2. HttpRequestHandlerAdapter
      • HttpRequestHandler 처리
    3. SimpleControllerHandlerAdapter(위 예시코드에 사용)
      • Controller Interface 처리

 

@RequestMapping 은 가장 높은 우선순위의 HandlerMapping인 RequestMappingHandlerMapping 과 가장 높은 우선순위의 HandlerAdapter인 RequestMappingHandlerAdapter 두가지를 사용하며 현대에 사용하는 Annotation 기반의 컨트롤러를 지원한다.

 

HttpRequestHandler로 알아보는 Spring MVC 동작 순서

// 인터페이스
public interface HttpRequestHandler {
	void handleRequest(HttpServletRequest request, HttpServletResponse response)
					throws ServletException, IOException;

}

 

// 구현체
@Component("/request-handler")
public class ExampleRequestHandler implements HttpRequestHandler {

	@Override
	public void handleRequest(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		System.out.println("request-handler Controller 호출");
		// 구현 로직
	}

}

Postman

출력결과

 

 

실행순서

  1. HandlerMapping 으로 핸들러 조회
    1. BeanName으로 Handler 조회(BeanNameUrlHandlerMapping 실행)
    2. ExampleRequestHandler 반환
  2. HandlerAdapter 조회
    1. HandleAdapter의 supports()를 우선순위 순서대로 호출
    2. HttpRequestHandlerAdapter가 HttpRequestHandler Interface를 지원한다
    • HttpRequestHandlerAdapter.supports()

  1. HandlerAdapter 실행
    1. DispatcherServlet이 조회한 HttpRequestHandlerAdapter를 실행하며 Handler 정보도 넘긴다
    2. HttpRequestHandlerAdapter 는 ExampleRequestHandler를 내부에서 실행 후 결과를 반환
    • HttpRequestHandlerAdapter.handle() → 단순히 handleRequest를 호출한다 = 오버라이딩된 handleRequest() 호출

 

DispatcherServlet에서 호출 → ha.handle()

 

 

 

View Resolver

📌 컨트롤러가 반환한 뷰 이름(View Name)을 실제 뷰 파일의 경로(View Path)로 변환하고, 클라이언트에게 응답할 화면(View)을 렌더링할 수 있도록 도와주는 컴포넌트입니다.

  • 반환된 ModelAndView 객체를 알맞은 View로 전달하기 위해 DispatcherServlet에서 ViewResolver를 호출하여 View 정보를 설정하는 역할을 수행한다.
  • 서버는 HTML 파일(또는 다른 뷰 파일)을 뷰(View)로 변환하여 클라이언트에게 응답으로 보낸다.는 말
package com.example.springbasicmvc.controller;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

// Spring Bean 이름을 URL로 설정
@Component("/view-controller")
public class ViewController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {

        System.out.println("view-controller가 호출 되었습니다.");

        // "test"는 논리적인 ViewName이다. ViewResolver가 물리적인 이름으로 변환해야 한다.
        return new ModelAndView("test");
    }
}
  • Template Engine JSP
    • webapp/WEB-INF/form.JSP

 

[sql입니다]
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <meta charset="UTF-8">
  <title>블로그 포스트 작성 페이지</title>
</head>
<body>
<h1>블로그 글쓰기</h1>
<form action="save" method="post">
  title: <input type="text" name="title" placeholder="제목" />
  content: <input type="text" name="content" placeholder="내용" />
  <button type="submit">저장</button>
</form>

</body>
</html>
  • ViewResolver
    • application.properties 설정
    • 설정을 기반으로 Spring Boot가 InternalResourceViewResolver 를 만든다.
[xml입니다]
spirng.mvc.view.prefix=/WEB-INF/views/
spirng.mvc.view.suffix=.jsp

 

localhost:8080/view-controller 호출

 

  • ViewName으로 View를 찾지 못하는 경우(View가 존재하지 않음)
@Component("/error-controller")
public class WhitelabelErrorController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("error-controller가 호출 되었습니다.");
				// viewName "sparta"는 존재하지 않는다.
        return new ModelAndView("sparta");
    }
}

 

  • 컨트롤러는 호출되지만 View를 못 찾아 Whitelabel Error Page를 응답한다.

 

 

 

Spring Boot의 ViewResolver

📌 Spring Boot를 사용하면 개발에 필요하여 자동으로 등록되는 ViewResolver들이 있다.

 

  • 우선순위 순서
    • 아래 두 가지 이외에도 많은 ViewResolver가 존재한다.
    1. BeanNameViewResolver
      1. Bean Name으로 View를 찾아 반환
    2. InternalResourceViewResolver(위 예시코드)
      1. application.properties 설정 파일에 등록한 prefix, suffix 설정 정보를 사용하여 ViewResolver 등록
// 아래 코드를 자동으로 해주는것과 마찬가지이다.
@Bean
InternalResourceViewResolver internalResourceViewResolver() {
	return new InternalResourceViewResolver("/WEB-INF/views", ".jsp");
}

 

 

InternalResourceViewResolver로 알아보는 Spring MVC 동작 순서

  1. HandlerAdapter 호출
    • HandlerAdapter를 통해 “test” 논리 View Name 얻음
  2. ViewResolver 호출
    • ”test” 이라는 View Name으로 viewResolver를 우선순위 대로 호출
      • BeanNameViewResolver는 View를 찾지 못한다.
      • InternalResourceViewResolver 호출
  3. InternalResourceViewResolver
    • InternalResourceViewResolver.buildView(String viewName)
    InternalResourceView 반환

 

4. InternalResourceView

  • JSP와 같이 서버에서 이동하는 forward()를 호출하는 경우와 같을 때 사용한다.

 

renderMergedOutputModel() → Model을 Request로 바꾼다.

 

5. view.render()

  • 외부에서 view.render()를 호출 후 ****RequestDispatcher를 가져와 forward()한다.

→ 매우 복잡한 구조를 가지고 있으니 모두 찾아볼 필요가 없습니다.

 

Thymeleaf는 View와 Resolver가 이미 존재한다. 라이브러리 의존성만 추가해주면 SpringBoot가 모두 자동으로 해준다. 즉, return “viewName”; 만으로 View가 Rendering 된다.

 

 

 

 

 

 

 

 

 

'Back-End (Web) > Spring' 카테고리의 다른 글

[Spring] Request Mapping  (1) 2024.12.16
[Spring] Spring Annotation  (1) 2024.12.15
[Spring] Spring Boot  (0) 2024.12.10
[Spring] Spring Framework  (2) 2024.12.09
[Spring] 웹 개발의 흐름  (1) 2024.12.05

+ Recent posts