NestJS 엔티티, 왜 다 public이야? – Spring 개발자의 궁금증 해결

2025. 5. 25. 16:28·Nest.js
728x90
반응형

NestJS로 프로젝트를 진행하면서 대부분의 코드를 GPT의 도움을 받아 빠르게 작성해왔다. 구조도 깔끔하고 실행도 잘 되니 별다른 주의 없이 넘어갔는데, 어느 순간 문득 엔티티나 서비스 클래스의 필드가 전부 public이라는 걸 뒤늦게 알아차렸다.
"어? 이거 괜찮은 건가?" 라는 생각이 들었고, Spring에서는 무조건 private에 getter/setter를 붙이던 습관 때문인지 더더욱 낯설게 느껴졌다.
이번 글에선 궁금증을 기반으로 학습한 내용을 java 와 비교해가며 작성한다.

1. NestJS와 Spring의 접근 제어자 차이: 캡슐화의 기본 철학이 다르다

Java(Spring)에서는 객체지향 원칙에 따라 엔티티의 모든 필드를 private으로 선언하고, 필드에 직접 접근하지 못하도록 getter/setter를 통해 우회하는 구조가 기본이다.

// java
public class User {
    private String name;

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

NestJS(TypeScript)는 반대다. 대부분의 Entity나 DTO는 public 필드로 구성되며, 오히려 getter/setter를 따로 만들지 않는다.

// type script
export class User {
  name: string;
}

NestJS에서는 캡슐화보다 간결한 구조 정의와 직렬화/역직렬화의 편의성을 우선시한다.


2. NestJS의 readonly는 Java의 final과 같다

Spring에서는 값을 수정할 수 없게 만들고 싶을 때 final을 사용한다. NestJS에서는 이 역할을 readonly가 담당한다.

// type script
export class User {
  readonly id: string; // 생성 후 변경 금지
}
// java
public class User {
    private final UUID id;

    public User(UUID id) {
        this.id = id;
    }
}
  • NestJS의 readonly는 생성자 이외의 위치에서는 값을 변경할 수 없다는 뜻이다.
  • 주로 id, 상수, 의존성 주입 필드에 사용된다.

3. 서비스 클래스의 생성자에서 private readonly는 어떤 의미인가?

NestJS에서는 의존성 주입을 이렇게 작성한다:

// type script
@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}
}
  • private: 외부에서 접근 불가
  • readonly: 주입된 후 재할당 금지

이 구조는 Spring의 private final과 정확히 매칭된다

public class UserService {
    private final UserRepository userRepository;

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

4. 하지만 진짜 차이는 여기서부터다 – DB 반영 시점의 처리 방식

접근 제어자는 스타일의 차이에 불과하다. 실무에서 NestJS를 사용할 때 가장 큰 차이는 "엔티티를 변경했는데 DB에 반영되지 않는다"는 것이다.

✅ Spring의 JPA는 변경 감지(Dirty Checking)를 자동 처리한다

// java
@Transactional
public void updateName(Long id, String name) {
    User user = userRepository.findById(id).orElseThrow();
    user.setName(name); // 필드 변경
    // save 호출 없이도 트랜잭션 종료 시 자동 반영
}
  • 트랜잭션 내에서 JPA가 엔티티 변경을 감지하고 DB에 반영
  • Persistence Context가 이를 가능하게 만든다

✅ NestJS의 TypeORM은 반드시 save()를 호출해야 한다

// type script
async updateName(id: string, name: string) {
  const user = await this.userRepository.findOneBy({ id });
  user.name = name;
  await this.userRepository.save(user); // save 없으면 DB 반영 안 됨
}
  • TypeORM은 Persistence Context가 없다
  • 엔티티를 수정하더라도 명시적으로 save를 호출하지 않으면 DB에는 아무 일도 일어나지 않는다

5. Spring과 NestJS의 Persistence 처리 방식 요약

항목 Spring (JPA) NestJS (TypeORM)
필드 접근 제어 private + getter/setter public, readonly
의존성 주입 필드 private final private readonly
Persistence Context 존재 없음
엔티티 변경 감지 자동 (Dirty Checking) 수동 (save 필요)
save 호출 필요 생략 가능 반드시 필요
트랜잭션 관리 @Transactional queryRunner or 데코레이터 기반

마무리

  • NestJS는 접근 제어보다는 구조적 선언과 직렬화에 초점을 맞춘다. 대부분의 Entity, DTO는 public이며, 의존성 주입은 private readonly 조합으로 처리한다.
  • readonly는 불변 필드, 생성자 주입된 의존성을 표현하는 용도로 매우 많이 사용된다.
  • 그러나 가장 주의해야 할 점은 DB 반영 처리 방식의 차이다.
    • Spring은 트랜잭션 안에서 필드만 바꿔도 자동 반영된다.
    • NestJS는 반드시 save()를 호출해야만 DB에 반영된다.
  • 따라서 NestJS에서는 변경이 필요한 순간을 명확하게 인지하고 save를 호출하는 습관이 중요하다.

    정리하자면, NestJS는 의도와 제어를 명확히 표현하는 프레임워크다. 자동으로 처리되는 것이 적은 만큼, 코드에 모든 흐름이 드러난다는 점에서 처음에는 낯설지만, 점차 "명시적인 것이 암묵적인 것보다 낫다"는 TypeScript의 철학과도 맞닿아 있다는 걸 느끼게 된다.

728x90
반응형

'Nest.js' 카테고리의 다른 글

[NestJS 테스트코드] Postgres, MongoDB 환경에서 유닛 & E2E 테스트 제대로 해보기  (2) 2025.07.09
[NestJS - 트러블 슈팅] 몽고 DB replicaSet 없이는 트랜잭션 불가능  (4) 2025.06.19
[NestJS 인증 흐름] Jwt 토큰 기반 인증 요청 흐름 정리  (1) 2025.05.22
[NestJS 응답/요청 변환기] 내부 camelCase, 외부 snake_case 통일하기  (0) 2025.05.22
[Nest.js] 제로베이스에서 1주일, 실전까지 반나절  (1) 2025.05.22
'Nest.js' 카테고리의 다른 글
  • [NestJS 테스트코드] Postgres, MongoDB 환경에서 유닛 & E2E 테스트 제대로 해보기
  • [NestJS - 트러블 슈팅] 몽고 DB replicaSet 없이는 트랜잭션 불가능
  • [NestJS 인증 흐름] Jwt 토큰 기반 인증 요청 흐름 정리
  • [NestJS 응답/요청 변환기] 내부 camelCase, 외부 snake_case 통일하기
highgarden
highgarden
커밋 하나하나가 쌓여 커다란 정원이 되는 중입니다. 하루하루 정성껏 심어가는 중 https://github.com/highgarden7
  • highgarden
    커밋심는 정원
    highgarden
  • 전체
    오늘
    어제
    • 분류 전체보기 (37)
      • ai (1)
      • devops (2)
      • Nest.js (14)
      • linux (14)
      • 네트워크 (6)
      • git (0)
      • aws (0)
      • docker (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    Chat GPT
    IP
    Java
    vercel
    nestjs
    springboot
    Linux
    E2E
    githib action
    네트워크
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
highgarden
NestJS 엔티티, 왜 다 public이야? – Spring 개발자의 궁금증 해결
상단으로

티스토리툴바