Skip to content

06 ‐ 06 ‐ 어드민 모듈 설계서

Hyeong Chan Kim edited this page Dec 7, 2023 · 4 revisions

비즈니스 요구사항

목적

  • 운영진이 사용자를 관리하고 데이터를 분석 할 수 있다.

요구사항

기능명세

  • 사용자 관리 << what?
    • 사용자 계정 생성
    • 사용자 정보 수정
    • 사용자 삭제
  • 데이터 분석
    • 서비스 이용 통계 시각화
    • 사용자 행동 분석 데이터 시각화
  • 서비스 설정
    • 시스템 설정 << what?
    • 공지사항 관리
    • 문의사항 처리 << what?

아키텍처 설계

모듈 구성

  • Controller : HTTP 요청을 처리한다.
  • Service : 비즈니스 로직 수행
  • Repository : 데이터 저장소와 상호작용을 담당한다.

API 엔드포인트 설계

  • 기획이 너무 추상적인데요

다른 모듈과 상호작용 포인트

tag 관련 결정 사항 정리(2023년 12월 이전)

  • tag는 여러 곳에서 다양하게 사용되고, 운영진에 의해 관리 된다.
  • 따라서 여러 곳에서 DI가 적절하게 이루어질 수도 있어야 하며, 업데이트, 삭제에 어느정도 되응이 잘 되어야 한다. 여러 상황을 고려, 성능적인 면을 고려하여 다음과 같이 정리한다.

tag 데이터 구조

tag : {
  tagId : Long,
  name : String, 
  color : String, // #이 들어간 경우와 CSS 예약워인 키워드 둘다 입력 가능하다. 
}

tag 사용의 구조

  1. tag 는 기본적으로 admin 모듈에서 관리 되며, 의존성 주입을 통해 모집글, team, skill 에 활용 된다.
  2. 이때 의존성 주입을 통해, 프론트엔드 측에서 검색, 해당 tag가 등록이 되는 경우 각 대응 하는 entity 들은 String 형 변수에 tag를 기록하는데 이때 방식은 다음과 같다.
  variable = "1^&%2^&%23^&%53"
  1. tagId 만을 기존의 문자열 방식으로 묶어서 기록하며, 사용자의 데이터 요청시 해당 string 을 분해하여 사용한다.
    • 이렇게 하는데는 이유가 있다.
    • 우선, 기술 태그 자체가 변동될 가능성, 추가될 가능성이 존재한다.
    • 더불어 기술이 사양될 수 있어 해당 태그가 사용되지 않게 바뀌거나, 이름이 변동되는 경우가 있을 수 있다.
    • 더불어 운영진이 관리하는 과정에서 데이터를 잘못 입력하는 경우가 발생할 수 있다.
    • 따라서, 이를 개선하고자 원래는 join을 통해 대상이 되는 entity들을 위한 tag 테이블을 구성하려고 했으나, 탐색 과정이 깊어지고, 최적화 및 성능 면의 문제, 해당 기능이 테이블을 추가 할당해 중복데이터 형태로 관리할 이유가 없는 데이터임을 감안안 중간 타협점으로 이렇게 지정한 것이다.
  2. 단, 몇 가지 해당 방식을 사용하는 모든 공간에서는 다음과 같은 기술적 제약을 건다.
    1. 반드시 tagId와 구분자를 붙여서 만들 것
      // 구분자를 활용한 코드 예시 
      @Transactional
      public void addKeyword(Authentication auth, String newKeyword) {
              User user = User.authenticationToUser(auth);
              String oldKeywords = user.getKeywordAlarm();
              String uniqWord = "\\^&%";
              String[] parts = null;
              if (oldKeywords != null) {
                  parts = oldKeywords.split(uniqWord); // 구분자 에러로 반드시 String으로 만들어서 집어 넣어줘야 정상적으로 split 이 가능하다. 
                  if (parts.length == 10) {
                      throw new BadRequestException("최대 등록 가능한 키워드는 10개입니다.");
                  }
              }
      ... 중략 
    2. tagId의 List 사이즈는 10을 초과해서는 안된다. 10을 초과하는 추가는 반드시 throw 를 통해 BadRequest로 제한한다.
    3. 반드시 이러한 tagList를 List<> 형태로 파싱을 하고는 Repository에서 실제 데이터를 받아와야 할 때는, 어떤 형태로든 한번에 찾아오는 조건문의 형태로 해야 한다. (JPA 메소드를 활용하든, EntityManager를 활용하든 반드시 1회 DB 탐색을 가능하게 할 것)
    4. 가능하면 권장되는 부분은 tagList의 DB의 레포지터리로의 개별 entity의 삭제, 내지는 갱신, 저장 시 @Async 어노테이션을 활용하여 비봉쇄, 비동기 전략으로 처리되도록 요청하고 기다리지 말고 함수 콜백이 돌아오도록 처리한다.(이는 권장사항이다)

2023-12-07 Tag 저장 방식 및 사용 구조 재정의

기존 구조의 문제점

Tag가 "1&2&3"으로 저장되어 있다고 가정했을 시, 만약 2가 삭제된다면 Tag에 삭제된 Id가 존재하는지 확인 한 후, 해당 Tag String을 "1&3"으로 업데이트 해주는 과정이 필요할 것이다. 여기서 문제가 발생하는데 삭제된 Id를 체크하고 이를 업데이트 해주는 함수를 작성을 해놓았다고 하더라도 이를 의존성을 주입한다던가 AOP를 통해 자동으로 수행해 줄 수 없다는 점이다. Entity에는 기본적으로 의존성 주입이 불가하고, Entity의 함수들에 대한 Aspect 적용 또한 스프링 AOP에서는 적용되지 않는 것으로 확인되었다. 그럼 Recruit라던가 User 같이 이 Tag들을 사용하는 Entity를 Get 해올때마다 이 함수를 수동으로 호출해줘야 한다는건데 이는 현재도, 그리고 앞으로도 피어에 속하는 모든 개발자가 이를 인지하고 있으면서 의도적으로 해당 함수 호출을 수행해줘야 한다는 점인데 이런 구조의 코드는 유지보수 측면에서 너무 저해되는 코드가 되어버린다.

개선안

그래서 결국 가장 기본적인 방법으로 되돌아가려고 한다. User와 Tag 테이블이 있으면 이 사이에서 양 쪽 테이블의 키를 가지는 junction table을 하나 두는 것이다.

  • User
    • id
  • Tag
    • id
  • user_tag
    • user_id
    • tag_id 위와 같은 구조로 테이블이 구성 될 것이고 Tag String의 정합성 검증이나 업데이트를 신경 쓸 필요없이 그냥 User나 Recruit 등 필요한 엔티티를 그냥 select 해오면 된다.
Clone this wiki locally