private Member(Long mbNo, String mbEmail, String mbName, String mbPassword, String mbMobile, LocalDateTime createdAt, LocalDateTime updatedAt, LocalDateTime withdrawalAt) {
this.mbNo = mbNo;
this.mbEmail = mbEmail;
this.mbName = mbName;
this.mbPassword = mbPassword;
this.mbMobile = mbMobile;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.withdrawalAt = withdrawalAt;
}
public static Member ofNewMember(String mbEmail, String mbName, String mbPassword, String mbMobile) {
return new Member(null, mbEmail, mbName, mbPassword, mbMobile, null, null, null);
}
@Test
@DisplayName("회원등록")
void save() {
// 새 회원 저장 시 데이터베이스에 회원 정보가 정상적으로 저장되는지 검증
// 저장 후, 데이터베이스에서 회원 정보를 조회하여 저장된 데이터와 일치하는지 확인
// 각 객체에 mbNo 없는 상태로 저장
Member member1 = Member.ofNewMember("marco@nhnacademy.com","마르코","12345","01012345678");
Member member2 = Member.ofNewMember("test@nhnacademy.com","테스트","12345","01011112222");
memberRepository.save(member1);
memberRepository.save(member2);
// mbNo 없는 상태로 저장했는데 저장 후 객체에 mbNo가 존재함.
System.out.println(member1.getMbNo() + "멤버 넘버!!!!!!!!!!!!!!!!!!!!!!");
System.out.println(member2.getMbNo() + "멤버 넘버!!!!!!!!!!!!!!!!!!!!!!");
Optional<Member> actualOptionalMember1 = memberRepository.findByMbNo(member1.getMbNo());
Optional<Member> actualOptionalMember2 = memberRepository.findByMbNo(member2.getMbNo());
Assertions.assertTrue(actualOptionalMember1.isPresent());
Assertions.assertTrue(actualOptionalMember2.isPresent());
log.debug("member1: {}", actualOptionalMember1.get());
log.debug("member2: {}", actualOptionalMember2.get());
Assertions.assertAll(
()->Assertions.assertNotNull(actualOptionalMember1.get()),
()->Assertions.assertEquals(member1.getMbEmail(), actualOptionalMember1.get().getMbEmail()),
()->Assertions.assertEquals(member1.getMbName(), actualOptionalMember1.get().getMbName()),
()->Assertions.assertEquals(member1.getMbPassword(), actualOptionalMember1.get().getMbPassword()),
()->Assertions.assertEquals(member1.getMbMobile(), actualOptionalMember1.get().getMbMobile()),
()->Assertions.assertNotNull(actualOptionalMember1.get().getCreatedAt()),
()->Assertions.assertNotNull(actualOptionalMember2.get()),
()->Assertions.assertEquals(member2.getMbEmail(), actualOptionalMember2.get().getMbEmail()),
()->Assertions.assertEquals(member2.getMbName(), actualOptionalMember2.get().getMbName()),
()->Assertions.assertEquals(member2.getMbPassword(), actualOptionalMember2.get().getMbPassword()),
()->Assertions.assertEquals(member2.getMbMobile(), actualOptionalMember2.get().getMbMobile()),
()->Assertions.assertNotNull(actualOptionalMember2.get().getCreatedAt())
);
}
출력 결과
870멤버 넘버!!!!!!!!!!!!!!!!!!!!!!
871멤버 넘버!!!!!!!!!!!!!!!!!!!!!!
테스트 코드에서 member1과 member2 객체를 생성할 때 mbNo(회원 번호)는 null로 설정됩니다. 하지만 memberRepository.save() 메서드를 호출한 이후에 mbNo에 값이 저장되는 이유는 레포지토리의 save() 메서드가 데이터베이스와 상호작용하면서 객체의 상태를 업데이트하기 때문입니다.
왜 mbNo가 저장되는가?
1. 데이터베이스에서 자동으로 생성된 ID
- 일반적으로 데이터베이스 테이블에서 기본 키(Primary Key)는 자동 증가(AUTO_INCREMENT) 속성을 가지며, 새로운 레코드가 삽입될 때 데이터베이스가 고유한 ID 값을 생성합니다.
- 예를 들어, MySQL에서 다음과 같은 테이블 정의가 있을 수 있습니다:
- save() 메서드가 호출되면, 데이터베이스에 새로운 레코드가 삽입되고, 이 과정에서 데이터베이스가 자동으로 ID 값을 생성하여 반환합니다.

2. 레포지토리의 save() 메서드 동작
- save() 메서드는 보통 다음과 같은 작업을 수행합니다:
- 객체를 데이터베이스에 저장:
- SQL INSERT 문을 실행하여 데이터를 삽입합니다.
- 객체를 데이터베이스에 저장:
예 :
INSERT INTO member (mb_email, mb_name, mb_password, mb_mobile)
VALUES ('marco@nhnacademy.com', '마르코', '12345', '01012345678');
-
- 생성된 ID를 객체에 설정:
- 데이터베이스는 새로 생성된 ID 값을 반환합니다(예: MySQL의 LAST_INSERT_ID()).
- 이 ID 값은 member1 객체의 mbNo 필드에 설정됩니다.
- 생성된 ID를 객체에 설정:
예 :
member1.setMbNo(generatedId);
3. JPA 또는 ORM 프레임워크의 동작
- 만약 프로젝트에서 JPA나 Hibernate 같은 ORM(Object-Relational Mapping) 프레임워크를 사용하고 있다면, 다음과 같은 방식으로 작동합니다:
- 엔티티 객체(Member)를 영속성 컨텍스트에 저장할 때, JPA는 데이터베이스에 INSERT 쿼리를 실행하고 자동 생성된 ID 값을 엔티티 객체의 필드(mbNo)에 매핑합니다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long mbNo;
테스트 코드에서의 흐름
- ofNewMember() 호출:
- member1과 member2 객체를 생성하며, 이때 mbNo는 null로 초기화됩니다.
- memberRepository.save() 호출:
- 새 회원 정보를 데이터베이스에 저장합니다.
- 데이터베이스는 각 회원에 대해 고유한 ID 값을 생성하고 반환합니다.
- 반환된 ID 값은 member1과 member2 객체의 mbNo 필드에 설정됩니다.
- 출력 결과:
- System.out.println(member1.getMbNo() + "멤버 넘버!!!!!!!!!!!!!!!!!!!!!!");
- 이 시점에서 mbNo는 더 이상 null이 아니며, 데이터베이스에서 생성된 고유한 값(예: 870, 871)이 출력됩니다.
왜 이런 설계가 필요한가?
- 데이터 무결성 보장:
- 회원 번호(mbNo)는 고유해야 하며, 이를 보장하기 위해 데이터베이스에서 자동으로 관리하는 것이 일반적입니다.
- 객체와 데이터베이스 동기화:
- 레포지토리의 save() 메서드는 데이터베이스와 애플리케이션 객체 간의 상태를 동기화하여, 애플리케이션이 항상 최신 상태의 데이터를 사용할 수 있도록 합니다.
- 편리한 테스트 및 개발:
- 테스트 코드에서는 ID 값을 직접 설정하지 않아도 되므로 간결한 코드를 작성할 수 있습니다.
- 동시에, 저장 후에는 자동으로 ID 값이 설정되므로 이후 로직에서 이를 활용할 수 있습니다.
결론
테스트 코드에서 member1.getMbNo()와 같이 출력했을 때 값이 존재하는 이유는:
- 데이터베이스가 새로운 회원 번호(mbNo)를 자동으로 생성했기 때문입니다.
- 레포지토리의 save() 메서드가 이 값을 반환받아 객체(member1, member2)의 필드에 설정했기 때문입니다.
이 설계는 데이터 무결성과 편리한 개발을 위해 일반적으로 사용되는 패턴입니다.
'Spring > 02. Repository' 카테고리의 다른 글
| 02. Repository Test (0) | 2025.02.11 |
|---|---|
| 01. Repository 개발 (1) | 2025.02.11 |