개요
올해 초에 진행한 이슈있슈 프로젝트를 회고해보려한다.
프로젝트가 끝나고 꽤 시간이 지났다.
느낀 점이 많았기에 언젠가 회고해야지~ 라고 생각했지만, 생각만 한 채로 반 년이 흘렀고,,
당시엔 블로그를 쓰지 않았기 때문에 이제서야 적는다 ㅎㅎ
프로젝트 주제
본 프로젝트는 소프트웨어공학 과목 Term Project이며 5명이서 약 5주간 진행했다.
Trac이나 Jira 등 기존 이슈 관리 시스템을 참고하여 시스템을 개발하는 게 과제 목표였다.
우리가 정한 요구사항을 간단히 설명하자면 다음과 같다.
- 모든 User는 프로젝트를 생성하고 초대할 수 있다.
- 각 User는 프로젝트에서 Admin, PL, Dev, Tester 중 하나의 역할을 가진다.
- 본인의 역할에 따라 다른 권한을 가진다 (이슈 생성, assign, fix, close 등)
쉽게 생각하면 게시판 서비스에 빗댈 수 있다.
프로젝트는 게시판이고, 이슈는 게시글에 해당한다.
사용자는 프로젝트에 특정 역할로서 참여하고
각 역할 별로 이슈 생성, 담당자 할당, 완료처리, 해결처리, 이슈종료 등등의 권한을 가진다.
문서화
팀원들 모두 웹 개발 경험이 부족했기에 최대한 설계를 미리 하고 개발에 들어가고자 했다.
과목도 과목인 만큼 문서화에 공을 들일 수 밖에 없었다.
(애초에 문서화 제대로 안하면 점수가 깎였다 😩)
회의 때 모여서 도메인 모델 만들고.. 클래스 다이어그램으로 바꾸고,,
유스케이스도 작성하고.. 첫 1~2주는 그림만 그렸던 것 같다.
이때 도출한 아티팩트는 노션에 정리해뒀다가 보고서 적을 때 활용했다.
API 명세서도 노션으로 관리했다.
그땐 Swagger를 모르던 범부였기에 API 명세서 관리하기 참 불편하구나~ 생각했던 기억이 난다.
Git 컨벤션
브랜치 전략은 Git flow를 사용했다.
당시엔 나 포함 깃 컨벤션을 지키는 게 처음인 팀원들이 대부분이여서
흔하기도 하고 이해하기도 쉬운 Git flow를 선택했다.
Git flow를 가져오되 프로젝트 규모를 고려해서 조금 간단화했다.
그림에서 보다시피 develop을 디폴트 브랜치로 썼고, main은 배포해도 되겠다 싶을 때 develop과 머지했다.
(기존 git flow의 release 브랜치 느낌으로)
이때 Squash merge를 알게 됐는데 이게 진짜 히트인 거 같다.
머지 커밋을 하면 기존 브랜치의 커밋이 다 합쳐짐 + 마지막에 Merge 커밋까지 생겨서 커밋 로그가 지저분해졌는데,
Squash merge 하면 기존 브랜치의 커밋들이 하나의 커밋으로 합쳐져서 커밋 로그를 깔끔하게 관리할 수 있다.
나의 역할은?
나는 회원 API 와 인증 및 인가 기능을 맡았다.
로그인 회원가입 같은 API랑 각 회원이 프로젝트에 접근할 때 인가 처리 클래스를 구현했다.
구현에는 Spring Security를 사용하였다.
인가는 메소드 레벨에서 처리했다.
인가 여부 확인을 위해선 (1) 어떤 사용자가 (2) 어떤 프로젝트에 (3) 어떤 기능을 사용하는지 확인해야 되는데
URL만으로는 이걸 확인할 수 없기 때문이다.
@PreAuthorize("hasAuthority(...)")
public ProjectDto updateById(...) {
...
}
결과적으로 요런 식으로 @PreAuthorize로 권한 확인할 수 있도록 만들었다.
주요 고민 사항
구현하는 과정에서 전공에서 배웠던 GRASP이나 SOLID 같은 객체지향원칙을 최대한 지키려고 노력했다.
그 중에서도 OCP를 특히 염두에 뒀다.
역할의 확장성
회원 관련 기능에서 확장성이 작용할 수 있으리라 짐작한 부분은 역할 및 권한이었다.
과제 요구사항에는 역할은 뭐가 있는지만 나와있었다.
그렇다면 역할을 기준으로 인가 여부를 판단할 수도 있을 것이다.
하지만 개발 도중 갑자기 역할 쪽 요구사항이 바뀐다면 어떻게 될지 생각해보았다.
/** 이슈 생성 */
@PreAuthorize("hasAuthority(#projectId, 'TESTER')")
public IssueDto reportIssue(...) {
...
}
. . .
/** 이슈 수정 */
@PreAuthorize("isReporter(#issueId)")
@PreAuthorize("hasAuthority(#projectId, 'TESTER')")
public IssueDto updateIssue(...) {
...
}
코드 예시
이런 식으로 짰다고 해보자.
갑자기 이런 요구사항이 주어진다면 어떻게 할까?
이슈 생성이랑 수정은 Tester 말고 Develop만 할 수 있게 바꿔주세요~
위의 코드를 찾아내서 Developer만 가능하도록 수정해야할 것이다.
이런 요구사항이 내려온다면 어떨까?
Tester가 가능한 일은 Developer도 전부 할 수 있게 바꿔주세요! ^^
여기저기 흩어진 Tester 권한 확인 코드를 전부 뒤져서 Developer도 가능하게 바꿔야 한다.
내가 생각한 해결책은 권한 개념을 추가하는 것이다.
권한은 앞서 살펴본 이슈 생성, 이슈 수정 등을 가리킨다.
그리고 역할은 내부적으로 권한의 집합을 가지도록 했다.
public enum Privilege {
ISSUE_REPORTABLE,
ISSUE_FIXABLE,
ISSUE_DELETABLE,
...
PROJECT_CREATABLE,
PROJECT_DELETABLE,
PROJECT_UPDATABLE,
...
}
위와 같이 권한 목록을 선언한 후
public abstract class Role {
protected Collection<Privilege> allowedPrivileges; // 허용된 권한 목록
...
}
public class Tester extends Role {
{
this.allowedPrivileges = List.of(
Privilege.ISSUE_REPORTABLE,
Privilege.ISSUE_UPDATABLE,
...
);
}
}
public class Developer extends Role {
{
this.allowedPrivileges = List.of(
Privilege.ISSUE_FIXABLE,
...
);
}
}
이런 식으로 Role 엔티티에 Privilege을 부여한다.
메소드 인가 여부는 사용자의 Role이 특정 Privilege을 갖는가?의 여부로 판단하도록 했다.
@Component("ProjectPrivilegeEvaluator")
public class ProjectPrivilegeEvaluator {
public boolean hasPrivilege(Long projectId, Privilege privilege) {
/* 사용자가 해당 프로젝트에 주어진 Privilege를 갖는지 확인 */
}
...
}
권한 확인을 수행하는 ProjectPrivilegeEvaluator 빈을 만들고,
/** 이슈 생성 */
@PreAuthorize("@PrivilegeEvaluator.hasPrivilege(#projectId, @Privilege.ISSUE_REPORTABLE)")
public IssueIdResponseDto reportIssue(...){
...
}
이런 식으로 인가 여부를 체크한다.
이러면 Developer가 이슈 생성, 수정도 가능하도록 변경해야 된다 해도
메소드를 찾아볼 필요 없이 Developer의 privilege 필드에 ISSUE_REPORTABLE,
ISSUE_UPDATABLE만 추가하면 된다.
public class Developer extends Role {
{
this.allowedPrivileges = List.of(
Privilege.ISSUE_FIXABLE,
Privilege.ISSUE_REPORTABLE, // 추가
Privilege.ISSUE_UPDATABLE, // 추가
...
);
}
}
위 예제에서는 권한이랑 역할을 enum으로 처리했다.
하지만 enum 대신 DB에 저장한다고 하면 코드를 재 컴파일할 필요도 없이 DB에 권한만 추가하면 될 것이다.
물론 이번 프로젝트는 과제에 불과해서 이런 요구사항이 실제로 떨어지진 않았다.
그치만 '과제니까 대충 돌아가기만 하면 되지~' 라고 어물쩡 넘기기엔 아쉽기도 했고
자주 변경이 일어날 영역에는 확장성을 고려해서 만들면 좋지 않을까 생각해서 이런 식으로 코드를 짰다.
고수들 입장에선 당연하게 느껴지겠지만, 본인은 원래 이런 거 신경도 안쓰고 우당탕탕 개발했었어서 나름 뿌듯했다. ㅎㅎ
회고
좋았던 점
좋은 설계에 대해 지속적으로 고민할 수 있어서 좋았다.
확장성에 대해 많은 고민을 하게하는 계기가 되었다.
팀원 간 소통이 원활히 이뤄진 점도 좋았다.
회의를 꽤 많이 했는데 다들 의견도 잘 내줬고 역할 분담도 순조로웠다.
첫 회의 때 좀 나대서 조장 비스무리한 역할을 어쩌다 맡게 됐는데,
조장이 없는 것과 마찬가지일 정도로 다들 잘 참여해줬다.
아쉬운 점
시간이 부족했던 게 아쉬움으로 남는다.
기간 자체가 5주로 넉넉하지 않기도 했는데, 다른 과목 과제까지 합쳐지면서 시간 투자를 많이 하지 못했다.
기능이 돌아가게 만드는 것에만 치우쳐서 시스템의 모든 구조에 대해 깊게 생각해보지 못한 건 아쉽게 느껴진다.
배운 점
Spring Security 작동 방식에 대해 배울 수 있었다.
완전히 파악한 건 아니여도 어떻게 짜면 잘 작동한다 정도는 알 수 있었다.
그 이상은 잘 몰라서 언젠가 날 잡고 공식문서든 뭐든 보면서 공부해야겠다.
깃허브 관련해서도 이것저것 배울 수 있었다.
이전에는 협업하더라도 정해진 규칙 없이 각자 브랜치 파고 merge하다가 충돌나는 게 다반사였다.
이전 협업 과제 때는 하도 충돌이 많이 나서 고치는데만 한세월 걸렸다.
컨벤션을 미리 정해놓고 작업하니까 커밋도 깔끔해지고 작업 상황도 한 눈에 볼 수 있어서 좋았다.
여담이지만 문서화의 중요성도 느꼈다.
프로젝트 진행한 지 반 년이 훨씬 지나서 기억에서 잊히고 있었는데
노션에 정리된 걸 보니까 당시 고민했던 내용을 다시 돌이킬 수 있었다.
문서화를 잘 해서 10년 뒤에 보더라도 바로 이해되도록 정리해두자.
Github 주소
'회고' 카테고리의 다른 글
[프로젝트] Owncast 회고 (0) | 2025.03.13 |
---|---|
[삼성전자 DX] 2025 상반기 S/W 알고리즘 특강 합격 후기 (1) | 2025.02.01 |