[이웃사이] Repository 관련 개념 및 구현

아양시·2022년 9월 14일
0

이웃사이

목록 보기
3/6

  초기에 설계를 제대로 해서 필요한 데이터와 형식이 뭔지 정리를 제대로 하고 시작했더라면 좋았겠지만 ,,
그러지 못했기 때문에 Repository, Service단은 우선 CRUD부터 간단히 구현하기로 했는데도 ,,
그마저도 뒷단을 구현하고 클라이언트와 소통하면서 수정사항이 계속 생겼다.



🙋‍♀️ Repository

repository단은 JpaRepository 인터페이스를 상속받아 사용하지 않고, 직접 EntityManager를 이용하여 구현했다.

📔 개념

1. @Repository

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {}
  • @Target : @Repository의 적용범위는 클래스, 인터페이스, enum이다.
  • @Retension : 런타임에 @Repository가 적용되고 유지된다. (Jvm 환경에서 실제로 사용하기 위함)
  • @Documented : JavaDoc 생성 시 @Repository에 대한 정보도 함께 생성해준다.
  • @Component : ComponentScan이 스캔할 때 해당 클래스의 Bean을 생성하여 ApplicationContext에 등록 해준다.

2. @RequiredArgsConstructor

2-1. 의존성 주입

https://kotlinworld.com/64

의존성 주입은 클래스에 대한 의존성의 인터페이스화를 통해 클래스 간 의존 관계를 약하게 하기 위한 것이다.

2-2. 의존성 주입 방법

https://shanepark.tistory.com/368

  의존성 주입 방법에는 생성자 주입, Setter 주입, 필드 주입이 있다.

  필드 주입으로 주입받는 클래스는 final로 선언 할 수 없어 수정 위험이 있기 때문에 필드 주입보다 생성자 주입이 권장된다.
또한 생성자 주입은 서로 다른 객체들 간의 순환 참조를 막을 수 있으며 단위 테스트에 비교적 효과적이다.

2-3. @RequiredArgsConstructor을 통한 생성자 주입

  @RequiredArgsConstructor은 final 또는 @NotNull이 붙은 필드의 생성자를 자동 생성해준다.
따라서 해당 어노테이션으로 필수적인 의존성들을 주입해 프로그램을 정상적으로 동작하도록 할 수 있다.


👩‍💻 구현

@Repository
@RequiredArgsConstructor
public class CommunityRepository {

    private final EntityManager em;

    public void save(Community community) {
        em.persist(community);
    }

    public Optional<Community> findById(Long id) {
        return Optional.ofNullable(em.find(Community.class, id));
    }

   ...

    public void delete(Community community) {
        em.remove(community);
    }
}

🤔 EntityManager 의존성 주입

  EntityManger를 통해 DB에 접근하는 메서드를 직접 구현했다. 이를 위해 EntityManger를 final로 선언하고, @RequiredArgsConstructor를 통해 생성자 주입을 한 후 사용했다.

🤔 CRUD 구현

  • 함수명은 JpaRepository의 함수명을 참고했다.
  • 생성 및 수정을 위해 entity 객체를 파라미터로 받아 영속화하는 save 함수를 만들었다.
  • id를 파라미터로 받아 객체 하나를 조회하는 findById 함수를 만들었다.
    • 조회한 객체가 없는 경우 exception 처리에 용이하게 하기 위해 Optional 타입으로 반환했다. (jdk 8 버전부터 Optional이 추가되었다.)
  • 삭제를 위해 entity 객체를 파라미터로 받아 영속성 컨텍스트에서 삭제하는 delete 함수를 만들었다.
    • service단에서 해당 객체의 존재 유무를 검사하여 repository 단에는 반드시 존재하는 객체만 들어오도록 보장한다.

🤔 필터링

public List<Community> findAllForAllWithCategory(Category category, String lineName, int page, int count, boolean desc) {}

public List<Community> findAllForAll(String lineName, int page, int count, boolean desc) {}

public List<Community> findAllForLineWithCategory(Category category, String lineName, int page, int count, boolean desc) {}

public List<Community> findAllForLine(String lineName, int page, int count, boolean desc) {}

  본 서비스에서 모든 게시글들은 공개범위를 가진다. 따라서 공개범위 별로 해당 데이터를 얻기 위해서는

  1. 전체 조회의 경우 : 공개범위가 ALL인 것 + 공개범위가 LINE인 것 중에 조회하는 사람과 작성자의 LINE이 같은 것
  2. 내 라인 조회의 경우 : 공개범위가 LINE인 것 중에 조회하는 사람과 작성자의 LINE이 같은 것

이렇게 나눠지기 때문에 조회하는 글의 공개범위 별로 메서드를 따로 만들었다.
ForAll이 붙은 메서드는 전체 조회, ForLine이 붙은 메서드는 내 라인 조회 시에 사용한다.

  또한, 커뮤니티의 경우 카테고리도 존재하기 때문에

  1. 카테고리가 ALL인 경우 : 카테고리에 상관없이 모든 데이터를 불러오기 때문에 WithCategory가 붙지 않은 메서드로 검색
  2. 카테고리가 ALL이 아닌 경우 : 카테고리로도 필터링을 해야하기 때문에 WithCategory가 붙은 메서드로 검색

하도록 만들었다.

  처음에는 재사용하기 쉬운 쿼리 메서드들을 만들어보고자 공개범위로만 조회하기, 공개범위랑 카테고리로 조회하기, 카테고리로만 조회하기 등등 일일이 만들었었는데, 페이징을 사용할 수가 없어서 결국 이렇게 갈아엎었다.

🤔 페이징과 정렬

  전체 데이터를 조회하는 서비스에서 페이징은 필수이다. 몇 억 개의 데이터가 존재할 경우, 서버와 클라이언트 사이에 몇 억 개의 데이터가 왔다갔다하는 불상사가 생기게 될 수 있기 때문이다. 즉, 어디부터 어디까지 몇 개의 데이터를 불러올지 클라이언트로부터 요청받아 그것만 제공하는 것이다.

public List<Community> findAllMine(Long profileId, int page, int size, boolean desc) {
        String query = "select c from Community c where c.writer.id = :profileId";
        if(desc) query += " desc";
        return em.createQuery(query, Community.class)
                .setParameter("profileId", profileId)
                .setFirstResult((page-1)*size)
                .setMaxResults(size)
                .getResultList();
    }

위 코드는 프로필 id로 해당 사용자가 쓴 글의 목록을 조회하는 메서드이다.

  • JPQL을 이용하여 작성했다.
  • page 번호와 한 page의 크기를 이용해 페이징 처리를 했다.
    • first results는 0번째부터 시작함에 유의하자.
  • desc라는 파라미터를 이용해 정렬 기준을 지정할 수 있도록 했다.

🤔 counting 메서드

  service layer에서 해당 요청한 목록 내 데이터 수를 알아야 하는 경우가 있어 counting 메서드를 추가하게 되었다.
조건별 조회 메서드마다 각각 counting 메서드를 작성하는 것이 상당히 번거로운 작업이었지만 우선은 그렇게 구현했고, 이후에 Querydsl을 이용하여 리팩토링 해볼 것이다.



참고 문헌

profile
BE Developer

0개의 댓글