🌀Entity

image.png

🌀DTO

@Data
public class MemberSignUpRequestDTO {

    private String email;
    private String password;
    private String name;
    private String profileImageBase64;
    private AddressDTO address;
    private ContactDTO contact;
    private Set<String> interests;
    private Set<String> skills;
    private String metadata;

}

🌀Controller

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class MemberRestController {

    private final MemberService memberService;

    @PostMapping("/members")
    public ResponseEntity<ResponseMemberDTO> createMember(@RequestBody MemberSignUpRequestDTO dto) {
        // DB에 저장
        ResponseMemberDTO saved = memberService.createMember(dto);
        return ResponseEntity.ok(saved);
    }

    @GetMapping("/members/{memberId}")
    public ResponseEntity<ResponseMemberDTO> getMemberById(@PathVariable Long memberId) {
        // DB에서 조회
        ResponseMemberDTO member = memberService.getMemberById(memberId);
        return ResponseEntity.ok(member);
    }

}

🌀Service

@RequiredArgsConstructor
@Service
public class MemberService {

    private final MemberRepository memberRepository;
    private final MemberMapper memberMapper;
    private final ApplicationEventPublisher eventPublisher;

    /**
     * @param memberDTO 회원 가입 요청
     * @return 회원 가입 응답
     * @apiNote 회원을 생성합니다.
     */
    @Transactional
    public ResponseMemberDTO createMember(MemberSignUpRequestDTO memberDTO) {
        **// 1) DTO -> Entity 매핑
        MemberEntity memberEntity = memberMapper.dtoToEntity(memberDTO);**

        // 2) 요청으로 넘어온 profileImageBase64를 무시하고,
        //    새로 가짜(랜덤) Base64 문자열을 만들거나, 필요한 로직을 수행
        String dummyBase64 = generateLargeDummyBase64();
        memberEntity.changeProfileImageBase64(dummyBase64);

        **// 3) DB에 저장
        MemberEntity savedMember = memberRepository.save(memberEntity);**

        // 4) 회원 생성 이벤트 발행
        MemberCreatedEvent memberSavedEvent = MemberCreatedEvent.of(savedMember.getId(), savedMember.getEmail());
        eventPublisher.publishEvent(memberSavedEvent);

        return memberMapper.entityToDto(savedMember);
    }

    /**
     * @param memberId 회원 ID
     * @return 회원 조회 응답
     * @apiNote 회원 ID로 회원을 조회합니다.
     */
    public ResponseMemberDTO getMemberById(Long memberId) {
        MemberEntity memberEntity = memberRepository.findById(memberId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid member ID"));

        return memberMapper.entityToDto(memberEntity);
    }

    // 테스트용으로 1MB 크기의 난수 데이터를 Base64로 만든 예시
    private String generateLargeDummyBase64() {
//        byte[] dummyBytes = new byte[1024 * 1024]; // 1MB
        byte[] dummyBytes = new byte[0]; // 10KB
        new SecureRandom().nextBytes(dummyBytes);
        return Base64.getEncoder().encodeToString(dummyBytes);
    }

🌀DTO 를 Entity로 변환

DTO 를 Entity로 변환해주기 위해서 mapstruct 라이브러리를 사용해서 매핑해준다.

/**
 * componentModel="spring"을 통해서 spring container에 Bean으로 등록 해 준다. (외부에서 주입받아서 사용하면 된다.)
 * unmappedTargetPolicy IGNORE 만약, target class에 매핑되지 않는 필드가 있으면, null로 넣게 되고, 따로 report하지 않는다.
 */
@Mapper(componentModel = "spring",
        unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface MemberMapper {

    // DTO -> Entity 변환 (생략된 매핑은 IGNORE 처리)
    @Mapping(target = "etcInfo.address", source = "address")
		@Mapping(target = "etcInfo.contact", source = "contact")
    @Mapping(target = "etcInfo.interests", source = "interests")
    @Mapping(target = "etcInfo.skills", source = "skills")
    @Mapping(target = "etcInfo.metadata", source = "metadata")
    MemberEntity dtoToEntity(MemberSignUpRequestDTO dto);

    // Entity -> DTO 변환
    @Mapping(target = "address", source = "etcInfo.address")
    @Mapping(target = "contact", source = "etcInfo.contact")
    @Mapping(target = "interests", source = "etcInfo.interests")
    @Mapping(target = "skills", source = "etcInfo.skills")
    @Mapping(target = "metadata", source = "etcInfo.metadata")
    ResponseMemberDTO entityToDto(MemberEntity entity);

    // 하위 객체 변환 메서드
    AddressDTO addressToAddressDTO(Address address);
    ContactDTO contactToContactDTO(Contact contact);
    }

🌀requestBody

회원 생성 api를 호출할 때는 아래와 같은 requestBody를 담아줘야한다.

{
 "email": "[email protected]",
 "password": "password",
 "name": "test",
 "profileImageBase64": "base64EncodedLongString...",
 "address": {
   "street": "123 Test St",
   "city": "Test City",
   "country": "Test Country",
   "postalCode": "12345",
   "additionalInfo": {
     "building": "A",
     "floor": "5",
     "doorCode": "1234"
   }
 },
 "contact": {
   "phone": "123-456-7890",
   "mobile": "098-765-4321",
   "workPhone": "111-222-3333",
   "emails": ["[email protected]", "[email protected]"],
   "socialMedia": {
     "twitter": "@testuser",
     "linkedin": "linkedin.com/testuser",
     "facebook": "fb.com/testuser"
   }
 },
 "interests": ["coding", "testing", "debugging"],
 "skills": ["java", "spring", "grpc"],
 "metadata": "{\"lastLogin\":\"2024-01-23\",\"status\":\"active\",\"rank\":\"senior\"}"
}