
@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;
}
@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);
}
}
@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로 변환해주기 위해서 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);
}
회원 생성 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\"}"
}