🧠 AI 브레인스토밍 플랫폼 개발 일지 - Day 3

📅 작업 일자

2025년 11월 26일 (Day 3)

🎯 오늘의 목표

  • Service 계층 완성
  • Service 테스트 코드 작성
  • DTO 계층 설계 및 구현

✅ 완료 사항

1️⃣ Service 계층 구현

3개 도메인의 Service 계층을 완성했다.

UserService

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    // 사용자 저장 (OAuth 자동 가입 시 사용)
    public User save(User user) {
        return userRepository.save(user);
    }

    // 아이디로 사용자 찾기
    public User findById(Long userId) {
        return userRepository.findById(userId)
                .orElseThrow(() -> new RuntimeException("아이디가 존재하지 않습니다."));
    }

    // 이메일로 사용자 찾기
    public Optional<User> findByEmail(String email) {
        return userRepository.findByEmail(email);
    }

    // 사용자 삭제
    public void delete(Long userId) {
        userRepository.deleteById(userId);
    }
}

특징:

  • @RequiredArgsConstructor로 생성자 주입
  • Repository 계층에 위임하는 단순 CRUD
  • 예외 처리 포함 (RuntimeException)

IdeaService

@Service
@RequiredArgsConstructor
public class IdeaService {
    private final IdeaRepository ideaRepository;

    public Idea save(Idea idea) { }
    public Idea findById(Long ideaId) { }
    public List<Idea> findByUserId(Long userId) { }
    public void delete(Long ideaId) { }
    public long countByUserId(Long userId) { }
}

InquiryService

@Service
@RequiredArgsConstructor
public class InquiryService {
    private final InquiryRepository inquiryRepository;

    public Inquiry save(Inquiry inquiry) { }
    public Inquiry findById(Long inquiryId) { }
    public List<Inquiry> findByUserId(Long userId) { }
    public void delete(Long inquiryId) { }
}

2️⃣ Service 테스트 코드 작성

테스트 프레임워크:

  • JUnit 5
  • AssertJ (가독성 좋은 검증)
  • Spring Boot Test

테스트 구조

@SpringBootTest
@Transactional  // 테스트 후 자동 롤백
class UserServiceTest {

    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;

    @Test
    @DisplayName("사용자 저장 테스트")
    void save() {
        // given
        User user = new User(...);

        // when
        User savedUser = userService.save(user);

        // then
        assertThat(savedUser.getUserId()).isNotNull();
        assertThat(savedUser.getEmail()).isEqualTo("test@example.com");
    }
}

작성한 테스트:

  • UserServiceTest (6개 테스트)
    • save, findById, findByIdNotFound, findByEmail, findByEmailNotFound, delete
  • IdeaServiceTest (5개 테스트)
    • save, findById, findByUserId, delete, countByUserId
  • InquiryServiceTest (4개 테스트)
    • save, findById, findByUserId, delete

테스트 패턴:

// given-when-then 패턴 사용
// AssertJ로 가독성 높은 검증
assertThat(result).isNotNull();
assertThat(list).hasSize(2);
assertThat(list).extracting("title")
    .containsExactlyInAnyOrder("title1", "title3");

3️⃣ DTO 계층 설계 및 구현

DTO가 필요한 이유:

  • Entity 직접 노출 방지 (보안)
  • API 스펙과 Entity 분리 (유지보수)
  • 필요한 데이터만 전달 (성능)

Request DTO vs Response DTO

RequestDto: 클라이언트 → 서버

  • 필요한 입력 데이터만 포함
  • toEntity() 메서드로 Entity 변환

ResponseDto: 서버 → 클라이언트

  • 반환할 데이터만 포함
  • from(Entity) 정적 메서드로 Entity → DTO 변환

UserRequestDto

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class UserRequestDto {
    private String email;
    private String username;
    private LoginProvider provider;
    private String providerId;

    public User toEntity() {
        return new User(
            null,           // userId 자동생성
            email,
            username,
            provider,
            providerId,
            MyRole.USER     // 기본값 설정
        );
    }
}

UserResponseDto

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class UserResponseDto {
    private Long userId;
    private String email;
    private String username;
    private LoginProvider provider;
    private LocalDateTime createdAt;

    public static UserResponseDto from(User user) {
        return new UserResponseDto(
            user.getUserId(),
            user.getEmail(),
            user.getUsername(),
            user.getProvider(),
            user.getCreatedAt()
        );
    }
}

완성된 DTO:

  • UserRequestDto / UserResponseDto
  • IdeaRequestDto / IdeaResponseDto
  • InquiryRequestDto / InquiryResponseDto

📊 프로젝트 현황

완료된 계층

✅ Entity (User, Idea, Inquiry)
✅ Repository (Spring Data JPA)
✅ Service (비즈니스 로직)
✅ Service Test (JUnit)
✅ DTO (Request/Response)

디렉토리 구조

src/main/java/com/brainstorming/brainstorming_platform/
├── domain/
│   ├── user/
│   │   ├── entity/User.java
│   │   ├── repository/UserRepository.java
│   │   ├── service/UserService.java
│   │   └── dto/
│   │       ├── UserRequestDto.java
│   │       └── UserResponseDto.java
│   ├── idea/
│   │   ├── entity/Idea.java
│   │   ├── repository/IdeaRepository.java
│   │   ├── service/IdeaService.java
│   │   └── dto/
│   │       ├── IdeaRequestDto.java
│   │       └── IdeaResponseDto.java
│   └── inquiry/
│       ├── entity/Inquiry.java
│       ├── repository/InquiryRepository.java
│       ├── service/InquiryService.java
│       └── dto/
│           ├── InquiryRequestDto.java
│           └── InquiryResponseDto.java
└── global/
    └── BaseEntity.java

src/test/java/
└── domain/
    ├── user/service/UserServiceTest.java
    ├── idea/service/IdeaServiceTest.java
    └── inquiry/service/InquiryServiceTest.java

💡 배운 점 & 트러블슈팅

1. JPA와 객체 참조

문제: save() 결과를 받지 않아도 원본 객체의 ID가 채워지는 이유?

Idea idea = new Idea(null, ...);
ideaService.save(idea);
// idea.getIdeaId()가 null이 아니다!

이유: JPA의 영속성 컨텍스트가 원본 객체에 ID를 자동으로 설정해주기 때문

2. DTO 필드 선택 기준

RequestDto에 포함하지 않는 것:

  • 자동 생성 필드 (PK, createdAt, updatedAt)
  • 서버에서 자동 설정하는 필드 (role, status)

ResponseDto에 포함하지 않는 것:

  • 보안 정보 (providerId, password)
  • 민감한 비즈니스 정보

3. AssertJ extracting 활용

List의 특정 필드만 검증:

assertThat(ideas)
    .extracting("userId")
    .containsOnly(1L);

assertThat(ideas)
    .extracting("title")
    .containsExactlyInAnyOrder("title1", "title3");

🎯 다음 단계 (Day 4)

우선순위 1

  • Controller 작성 (REST API)
  • Postman으로 API 테스트
  • 예외 처리 개선

우선순위 2

  • Controller 테스트 (MockMvc)
  • Swagger 문서화
  • Validation 추가 (@Valid)

우선순위 3

  • Python FastAPI 연동
  • OAuth2 로그인 구현

📚 참고 자료

  • 김영한님 - 스프링 부트와 JPA 실무 완전 정복 로드맵
  • Spring Boot 3.5.7 공식 문서
  • AssertJ 공식 문서

🔗 GitHub

  • Repository: brainstorming-platform
  • Commit: Day 3 - Service, Test, DTO 완성

Day 3 완료! 수고하셨습니다! 🎉

+ Recent posts