게시판 서비스 - 요구사항 분석, Repository 설계

Sarah·2026년 4월 27일

요구사항 분석

  • 회원 관리 (User)
    • 회원 가입
    • 회원 정보 수정
    • 로그인 / 로그아웃 기능
    • 회원 조회
  • 커뮤니티 (Board & Comment)
    • 게시글 : 작성 / 수정 / 삭제 / 상세 조회
    • 댓글 : 작성 / 수정 / 삭제
    • 페이징 처리 : 게시글 목록 조회 시 성능 고려한 페이징 구현
  • 검색 및 필터링 (Search)
    • 게시글 제목의 키워드로 검색
    • 작성자로 게시글 검색
  • 프로젝트 시작 초기에는 간단히 회원/게시글/댓글로 CRUD만 구현해보려 했지만 구현하다보니 로그인/검색/페이징 기능이 추가되었다.

Repository 설계

User Repository

@Repository
@RequiredArgsConstructor
public class UserRepository {

    private final EntityManager em;
    
    public void save(User user) {
        em.persist(user);
    }

    public Optional<User> findOne(Long userId) {
        return Optional.ofNullable(em.find(User.class, userId));
    }

    public List<User> findAll() {
        return em.createQuery("select u from User u", User.class).getResultList();
    }

    public Optional<User> findByEmail(String email) {
        List<User> users = em.createQuery("select u from User u where u.email = :email", User.class)
                .setParameter("email", email)
                .getResultList(); // Stream 대신 List로 확실히 받아오기

        return users.stream().findFirst(); // 리스트에서 첫 번째를 Optional로 반환
    }
}

Board Repository

@Repository
@RequiredArgsConstructor
public class BoardRepository {

    public Optional<Board> findOne(Long boardId) {
        return em.createQuery("select b from Board b join fetch b.user where b.id = :boardId", Board.class)
                .setParameter("boardId", boardId)
                .getResultStream().findFirst(); // JPQL은 List 형태로 반환하려고 하기 때문에 Optional로 감싸기 위한 방식
    }

    public List<Board> findAll() {
        return em.createQuery("select b from Board b join fetch b.user order by b.createdAt desc", Board.class).getResultList();
    }
    
    public List<Board> findByUser(Long userId) {
        return em.createQuery("select b from Board b join fetch b.user where b.user.id = :userId", Board.class)
                .setParameter("userId", userId)
                .getResultList();
    }

    public List<Board> findByTitle(String keyword) {
        return em.createQuery("select b from Board b join fetch b.user where b.title like : keyword", Board.class)
                .setParameter("keyword", "%"+keyword+"%")
                .getResultList();
    }

  • 순수 JPA
    • 스프링 데이터 JPA가 아닌 순수 JPA를 사용하면서 @RequiredArgsConstructor 생성자 주입을 사용해 EntityManager를 주입 받았다.
    • em.createQuery 메서드를 이용해 JPQL 구현
  • Optional<> 사용
    • findOne 이나 findByEmail 같이 단 건 조회하는 경우에 DB에 데이터가 없으면 Null이 반환되는데, Service 계층에서 null을 체크하여 NullPointerException을 방지하고, orElseThrow 로 예외 처리가 가능하도록 하였다.
  • Fetch Join
    - 단순 Join 대신 Fetch Join을 사용하여 연관된 엔티티를 한 번의 쿼리로 묶어서 가져오도록 리파지터리 코드 설계
profile
헤맨 만큼 내 땅

0개의 댓글