[Spring] 4일차

김민지·2026년 4월 14일

kosa-Spring

목록 보기
4/13

유효성 검사

반드시 DTO로 받아야 한다.

로그인, 회원가입 실습

@NotNull, @Valid, @Size, @Pattern

유효성 검사는 필수!


1. Spring Bean & DI

Spring Bean이란

  • Spring Bean은 id(name)로 관리되는 싱글톤 객체
    • 싱글톤: 디자인 패턴중에 하나로, 클래스의 객체를 한개만 생성해서 그 객체만을 사용하는 패턴이다.
    • 예를 들면 우리가 간단한 계산기 기능을 가진 클래스를 만든다고 가정해보자! 이 클래스에서는 계산만 하고 결과값만 반환하는 메서드만 있을것이다. 즉 상태값이 없고 기능만을 수행하는 클래스라고 볼수있다. 이를 사용하려고 여러곳에서 객체를 생성하면 낭비라는 생각이 들지 않는가?  이런 경우에 싱글톤 패턴을 통해 객체를 한개만 생성하고 이를 돌려쓴다는 거다. 즉 메모리적으로 효율이 좋아진다는 거다!
    • 출처: https://skianything.tistory.com/24
  • WebApplicationContext(IoC 컨테이너)에서 빈을 관리

생성 방법

아래 어노테이션이 붙은 클래스는 자동으로 Bean 등록

name이 주어지지 않으면 클래스 이름의 첫자를 소문자로 한 이름을사용

@RestController("name") // 컨트롤러 객체 생성(요청 처리 담당)
@Service("name") // 서비스 객체 생성(비즈니스 로직 처리 담당)
@Repository("name") // DAO 객체 생성 (DB 연동 처리 담당)
@Component("name") // 기타 기능을 제공하는 객체 생성
@Mapper // MyBatis 매퍼 객체 생성 (DB 연동 처리 담당)

프로그램 방식: @Bean

@Bean이 적용된 메소드 이름이 id(name)이 되고, 리턴 객체가 Bean이 된다.

@Configuration이 적용된 클래스 내에서 작동

@Configuration
public class AppConfig {
	public BeanType getBean() {
		return new BeanType();
	}
}

선언적 방식은 내가 직접 작성한 클래스일 때 사용하고, 프로그램 방식은 내가 바꿀 수 없는 외부 라이브러리나 클래스를 사용할 때 사용한다. 보통 전자를 훨씬 많이 사용함.

2. 의존 주입(DI)

이미 외부에서 만들어져 있는 것을 가져다가 쓰는 방식

주입 대상

  • @Service, @Repository, @Component가 적용된 클래스의 빈
  • @ Configuration 클래스의 @Bean이 적용된 메소드의 반환 빈

필드 주입: @Autowired 이용

생성자 주입: 매개변수 이용해서 빈 주입

3. DI(Dependency Injection) 정리

DI(의존성 주입)란?

객체가 필요한 다른 객체를 직접 만들지 않고, 외부에서 주입받는 것

// ❌ DI 없이 직접 생성
public class DiMemberService {
    private DiAComponent aComponent = new DiAComponent(); // 직접 생성
}

// ✅ DI 사용 - Spring이 만들어서 넣어줌
public class DiMemberService {
    @Autowired
    private DiAComponent aComponent; // Spring이 주입
}

왜 좋냐면? 결합도가 낮아져서 나중에 구현체를 바꿔도 DiMemberService 코드를 안 건드려도 됨.


핵심 개념 3가지

1. @Component / @Service → Bean 등록

Spring 컨테이너: "이 클래스들은 내가 관리할 객체(Bean)야!"
어노테이션의미
@Component일반 컴포넌트
@Service서비스 계층

둘 다 Spring Bean으로 등록된다는 점은 동일.

2. @Autowired → 주입

Spring이 타입을 보고 맞는 Bean을 자동으로 꽂아줌.

3. @Qualifier → 구분자

같은 타입의 Bean이 2개 이상일 때 어떤 걸 쓸지 이름으로 지정.


❗ 문제 상황 — 왜 @Qualifier가 필요한가

// DiInterface를 구현한 클래스가 2개!
@Component DiInterfaceImplA implements DiInterface
@Component DiInterfaceImplB implements DiInterface
@Autowired
private DiInterface impl; // ← Spring: "A 줄까? B 줄까? 모르겠는데!" → 💥 오류

그래서 @Qualifier로 이름을 지정해줘야 함.

Bean 이름은 기본적으로 클래스명 앞글자 소문자diInterfaceImplA, diInterfaceImplB


주입 방식 2가지 비교

필드 주입 (@Autowired 필드에 직접)

@Autowired
private DiAComponent aComponent;

@Autowired @Qualifier("diInterfaceImplA")
private DiInterface implA;
  • 코드가 짧고 간단
  • 단점: 테스트하기 어렵고, final 불가

생성자 주입 (권장 방식 ✅)

private DiBComponent bComponent;
private DiInterface implB;

public DiMemberService(DiBComponent bComponent, @Qualifier("diInterfaceImplB") DiInterface implB) {
    this.bComponent = bComponent;
    this.implB = implB;
}
  • Spring이 이 클래스를 만들 때 생성자 호출 → 그때 파라미터를 자동으로 주입
  • 실무에서 권장하는 방식 (final 가능, 테스트 용이)

join() 실행 흐름

public void join() {
    log.info("실행");          // DiMemberService.join 실행됨
    aComponent.method();      // DiAComponent의 method()
    bComponent.method();      // DiBComponent의 method()
    implA.method();           // DiInterfaceImplA의 method() → "실행" 로그
    implB.method();           // DiInterfaceImplB의 method() → "실행" 로그
}

💡 인터페이스를 쓰는 이유

private DiInterface implA; // DiInterfaceImplA 타입이 아니라 인터페이스 타입으로 받음

나중에 ImplAImplC로 교체해도 DiMemberService 코드를 전혀 안 바꿔도 됨.
Bean만 바꾸면 끝 → 이게 DI + 인터페이스의 핵심 장점!


4. 프로퍼티 주입

properties 작성 위치

application.properties에서 직접 작성

String Bean 객체 내부에 주입: @Value

필드, 생성자 매개변수에 적용할 수 있음.

@Value(”${api.key}”) private String key;

5. Database 연동

DAO (Data Access Object)

  • Spring 빈으로 자동 생성되도록 @Mapper 어노테이션을 붙인다.
  • Mapper XML에서 루트 엘리먼트의 namespace에 인터페이스의 전체 이름을 기술한다.
  • 테이블당 1개씩 작성하는 것을 원칙으로 한다.
  • 메소드 이름은 Mapper XML의 엘리먼트(태그) id 값으로 작성한다.
  • 컬럼의 값을 따로 매개값으로 받기 보다는 행을 표현하는 DTO(VO)로 받는 것이 좋다.
  • insert, update, delete 메소드의 리턴값은 반영된 행수로 하는 것이 좋다.
  • insert 후, 자동 생성된 키는 DTO에 세팅해서 Service에서 이용할 수 있도록 한다.

6. DTO (Data Transfer Object)

개념

계층 간 데이터를 전달하기 위한 객체.

왜 필요한가?

Spring MVC 계층 구조:

Client ↔ Controller ↔ Service ↔ Repository ↔ DB

Entity(DB 테이블과 매핑된 객체)를 그대로 노출하면 문제가 생긴다:

  • DB 구조가 외부에 그대로 노출됨
  • 필요 없는 필드까지 전송됨
  • 보안에 민감한 필드(비밀번호 등)가 노출될 수 있음

→ DTO를 따로 만들어 필요한 데이터만 담아 전달한다.


Entity vs DTO

// Entity — DB 테이블과 매핑
@Entity
public class User {
    private Long id;
    private String username;
    private String password;   // 절대 외부에 노출하면 안 됨
    private String email;
    private LocalDateTime createdAt;
}

// DTO — 클라이언트에 전달할 데이터만
public class UserResponseDto {
    private Long id;
    private String username;
    private String email;
    // password, createdAt은 제외
}
EntityDTO
목적DB 매핑계층 간 데이터 전달
필드테이블 전체 컬럼필요한 것만
노출외부 노출 X외부 전달용 O

실제 흐름

Client → [UserRequestDto]  → Controller → Service → Repository → DB
Client ← [UserResponseDto] ← Controller ← Service ← Repository ← DB

요청용(RequestDto)과 응답용(ResponseDto)을 분리해서 만드는 것이 일반적이다.


핵심 특징

  • 로직 없음 — getter/setter만 있는 순수한 데이터 운반체
  • 필요한 필드만 — 목적에 맞게 필드를 골라서 구성
  • Lombok과 잘 쓰임@Getter, @Setter, @AllArgsConstructor 등으로 보일러플레이트 제거

한 줄 정리

Entity는 DB용, DTO는 통신용

profile
안녕하세요

0개의 댓글