패스트캠퍼스 데브캠프 : 남궁성의 백엔드 개발 3기

record vs @Data, @RestController vs @Controller, @RequiredArgsConstructor, Stream, JDBC, 테스트 코드까지 실무 예제로 정리

Tech_JINI 2025. 4. 29. 17:27

✅ @Data vs record: DTO 작성 방식 비교

Lombok의 @Data

@Data
public class MemberDto {
    private Long id;
    private String email;
    private String nickname;
    private LocalDate birthday;
}

 

  • @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor 등을 한 번에 생성해주는 Lombok 애노테이션입니다.
  • 가변(mutable) 객체를 만들 수 있음.

 

Java 14+의 record

record는 Java 14 이상에서 사용할 수 있는 간단한 DTO 정의 방식입니다. 모든 필드는 final로 선언되며, 불변성을 자동으로 보장합니다.

 

public record MemberDto(
        Long id,
        String email,
        String nickname,
        LocalDate birthday
) {}

 

 

  • final 필드와 자동 생성 생성자, toString, equals, hashCode 제공
  • 불변(immutable) 객체

 

항목  @Data   record
불변성 ❌ (가변 객체) ✅ (불변 객체)
생성자 수동 또는 @RequiredArgsConstructor 필요 자동 생성
lombok 의존성 필요 불필요 (Java 표준 기능)
유연성 OOP에서 확장성 높음 상속 불가, 제한적 사용
DTO에 적합성 API 응답용 DTO, 가변성 필요한 경우 조회 전용 DTO, 불변 데이터 구조

 


✅ @RestController vs @Controller

@RestController

  • @Controller + @ResponseBody 조합과 동일
  • API 응답을 JSON으로 반환할 때 사용

@Controller

  • View 템플릿(.jsp, .html 등) 렌더링을 위한 컨트롤러

👉 언제 사용하나요?

  • REST API 서버에서는 @RestController
  • Spring MVC 기반 페이지 렌더링 앱에서는 @Controller

 


✅ @RequiredArgsConstructor

이 애노테이션은 final로 선언된 필드에 대해 생성자를 자동 생성해주는 Lombok 기능입니다.

@RequiredArgsConstructor
@RestController
public class HelloWorldController {
    final private NamedParameterJdbcTemplate jdbcTemplate;
}

 

장점

  • 불변 필드 + 생성자 주입으로 의존성 주입의 안정성 증가
  • 깔끔한 코드 유지

🤔 @RequiredArgsConstructor: 왜 생성자 자동 생성이 유리할까?

✅ 왜 좋을까?

  • 불변 필드 보장: final은 한 번만 주입 → 객체 무결성 향상
  • 테스트 쉬움: 명시적 생성자가 있으니 Mockito 등에서 주입이 쉬움
  • 안정성: 필드 주입(@Autowired)보다 생성자 주입이 순환참조 방지에 좋음

✅ RowMapper와 NamedParameterJdbcTemplate

RowMapper는 ResultSet을 객체로 변환하는 인터페이스입니다.

static final RowMapper<MemberNicknameHistory> rowMapper = (ResultSet rs, int rowNum) -> MemberNicknameHistory
    .builder()
    .id(rs.getLong("id"))
    .memberId(rs.getLong("memberId"))
    .nickname(rs.getString("nickname"))
    .createdAt(rs.getObject("createdAt", LocalDateTime.class))
    .build();

 

NamedParameterJdbcTemplate은 명명된 파라미터로 쿼리를 수행할 수 있도록 도와주는 템플릿 클래스입니다.

var sql = "SELECT * FROM MemberNicknameHistory WHERE memberId = :memberId";
var params = new MapSqlParameterSource().addValue("memberId", memberId);
return namedParameterJdbcTemplate.query(sql, params, rowMapper);

장점

  • 가독성 높고 유지보수 쉬운 쿼리
  • 동적 파라미터 구성에 적합

 


✅ stream().map(this::toDto)와 Optional

Stream 활용 예시

List<MemberDto> result = members.stream()
    .map(this::toDto)
    .toList();
  • map()은 변환 메서드 (예: 엔티티 → DTO) 에 자주 사용됨

 

🚀 Stream의 장점: 왜 써야 할까?

Java Stream은 컬렉션을 함수형으로 처리할 수 있게 해주는 도구입니다.

 

기능 설명
간결성 for 루프 없이 짧게 표현 가능
가독성 데이터 흐름(→변환→필터→수집)이 자연스럽게 표현됨
체이닝 여러 연산(map, filter, sort...)을 연결해 가독성 높임
병렬 처리 .parallelStream()으로 쉽게 병렬화 가능
불변성 원본 데이터를 수정하지 않음 (Functional style)

 

 

Optional

Optional<Member> maybeMember = repository.findById(id);
maybeMember.ifPresent(member -> ...);

 

  • NPE 방지
  • null 처리를 더 명시적으로

✅ Object Mother vs EasyRandom

EasyRandom (실제 사용 예)

EasyRandomParameters param = new EasyRandomParameters();
Member member = new EasyRandom(param).nextObject(Member.class);

 

 

테스트에서 객체 생성 자동화를 위한 라이브러리입니다. 다양한 속성을 랜덤하게 채워주어 테스트 생산성을 높여줍니다.

Object Mother 패턴

반복되는 테스트 객체 생성을 메서드로 추상화한 패턴입니다.

public class MemberFixtureFactory {
    public static Member create() {
        return new EasyRandom().nextObject(Member.class);
    }
}

 

차이점

항목 EasyRandom Object
생성 방식 자동화된 랜덤 데이터 명시적으로 정의된 고정값
장점 빠르고 다양한 케이스 생성 통제된 테스트 데이터 생성
사용 위치 빠른 테스트용 검증된 구조 테스트용