스프링 부트 - 정리 - 1 : HTTP 요청부터 응답까지

728x90

 

실제 HTTP 요청 → 서버 내부 레이어 → DB → 응답 반환까지,
스프링부트에서 DAO(Repository), Entity, DTO, Service가 어떻게 흘러가는지 실제 코드 흐름은 아래와 같다


✔ 전체 구조 (먼저 큰 흐름)

 
[클라이언트]
  ↓ HTTP 요청
[Controller]
  ↓
[Service]
  ↓
[DAO / Repository]
  ↓
[Entity]
  ↓
[DB]

그리고 응답은 반대로 올라옵니다.


✔ 상황

👉 회원 조회 API

 
GET /users/1

 


1️⃣ 클라이언트 → HTTP 요청

예시 (curl)

curl http://localhost:8080/users/1

2️⃣ Controller (HTTP 입구)

👉 HTTP 요청 받는 역할

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

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public UserResponse getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }
}

✔ 역할

  • URL 매핑
  • HTTP 요청 파싱
  • Service 호출
  • Response 반환


3️⃣ Service (비즈니스 로직 담당)

👉 실제 서비스 로직 처리

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public UserResponse getUser(Long id) {

        User user = userRepository.findById(id)
                .orElseThrow();

        return new UserResponse(user.getName(), user.getEmail());
    }
}

✔ 역할

  • 비즈니스 로직
  • 트랜잭션 관리
  • Entity → DTO 변환


4️⃣ DAO / Repository (DB 접근 담당)

👉 DAO 역할

 
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

✔ 내부에서 하는 일

Spring Data JPA가 자동으로 아래 SQL 실행

 
SELECT * FROM users WHERE id = 1


5️⃣ Entity (DB 테이블 매핑)

 
@Entity
@Table(name = "users")
public class User {

    @Id
    private Long id;

    private String name;
    private String email;

    protected User() {}

    public String getName() { return name; }
    public String getEmail() { return email; }
}

 


👉 DB 테이블과 1:1 연결


 

6️⃣ DTO (응답용 객체)

👉 클라이언트에게 전달되는 데이터

public class UserResponse {

    private String name;
    private String email;

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

    public String getName() { return name; }
    public String getEmail() { return email; }
}

 


✔ 실제 전체 실행 흐름 (타임라인)


① HTTP 요청 발생

 
GET /users/1

② Controller 실행

 
UserController.getUser(1)

③ Service 호출

 
userService.getUser(1)

④ Repository 호출

 
userRepository.findById(1)

⑤ JPA가 SQL 실행

 
SELECT * FROM users WHERE id = 1

⑥ DB → Entity 생성

 
User 객체 생성

⑦ Service에서 DTO 변환

 
User → UserResponse

⑧ Controller → JSON 응답

 
{ "name": "홍길동", "email": "hong@test.com" }


✔ 시각 흐름도

 
[Browser / curl]
        │
        ▼
Controller (HTTP 처리)
        │
        ▼
Service (비즈니스 로직)
        │
        ▼
Repository / DAO (DB 접근)
        │
        ▼
Entity (테이블 매핑)
        │
        ▼
DB


✔ 각 레이어 존재 이유

Controller

👉 HTTP 전용 처리


Service

👉 로직 중심


DAO / Repository

👉 DB 접근 분리


Entity

👉 DB 구조 표현


DTO

👉 외부 공개 데이터



✔ 왜 이렇게 나누냐 (핵심 이유)

유지보수

레이어별 수정 가능


테스트

Service만 단위 테스트 가능


보안

Entity 그대로 노출 방지


확장성

DB 교체 쉬움



✔ 정리 > HTTP 하나가 지나갈 때

보통 이런 객체들이 생성됩니다.

 
Request DTO
   ↓
Controller
   ↓
Service
   ↓
Entity
   ↓
Repository
   ↓
DB
   ↓
Entity
   ↓
Response DTO

 


✔ 간단 정리

스프링부트에서 HTTP 요청은 Controller에서 시작되어 Service에서 비즈니스 로직을 처리하고, Repository(DAO)를 통해 DB에 접근하며, Entity로 DB 데이터를 매핑한 뒤 DTO로 변환되어 클라이언트에게 응답된다. 이러한 계층 분리는 보안, 유지보수성, 테스트 용이성, 확장성을 확보하기 위해 사용된다.

 

✔ 한줄 소감

레이어 분리는 단순한 구조 나누기가 아니라 안정성과 유연성을 위한 설계 철학이라는 점을 이해하게 되었다.

 

728x90