넘블에서 진행하는 스프링부트 웹소설 클론코딩 과제를 진행하였다.
당초 목적은 당연히 과제의 필수 구현목록들을 전부 구현하는 것이였지만, 막상 개발을 시작하면서 기초적인 것부터 많이 부족하다는 것을 알게되었다. 공부는 많이 안되었지만, 일단 부딪혀보자는 생각으로 등록한 챌린지였으니 당연한 결과였겠다... 결국 일부 내용들(기초적인 API)만 구현하였지만, 2주간 진행하며 뜻깊었던 첫 프로젝트 경험이라 배운 내용들을 정리해보고자한다.
넘블에서 진행한 프로젝트를 진행하는 과정이 생소했다. 개인 프로젝트로 진행되지만, 원한다면 팀 매칭을 통해 스터디 등을 진행하고, Scrum Board 등을 활용해 진행사항을 확인하는 등으로 팀과 함께 과제에 대한 시각을 공유하고 계획할 수 있었다.
나는 항상 혼자서 무언가 해보려던 습관을 버리고 사람들과 만나 얘기해보고 배워보고 싶어서 팀으로 참가하였고, 디스코드를 통해 회의를 하면서 내가 생각도 못하고 있었던 프로젝트 설계법들에 대해 알게되었다.(사실 여태 시도도 하지 않았던 것 같긴 하다...)
A. MVP 기능 정리
MVP란 Minimum Viable Product의 약자로, 최소 기능 제품을 의미한다. 따라서 웹소설 서비스를 이용할 때의 기능들을 정리하는 과정들이 필요했다. 과제 문서에서 제공되는 기능들에 대한 설명들도 있었지만, 이를 조금 더 구체화 하고 정밀하게 설계할 필요가 있었다.
B. ER Diagram 설계
ER Diagram이란 Entity-Relationship 모델을 표현하는 방법으로, 위와 같이 데이터베이스에서 상관관계를 도식화하는 것을 의미한다. 이를 미리 작성함으로써 추후에 데이터베이스를 설계하는데 시간을 절약할 수 있었다. 나의 경우 회의때 테이블에서 중복되는 내용들이 많은 것들은 따로 테이블로 분리하는 것이 좋다는 피드백을 받아 Novel 테이블과 Series 테이블을 분리하였다.
C. API 문서 정리
어떤 URI로 이동하였을 때 어떤 데이터를 서버에 제공해야하고, 어떤 데이터를 응답받을 것인지 정리해둔 문서이다. 나는 이를 조금 소홀히 하였는데, 본격적으로 코드를 작성하다보니 점점 이름도 복잡해지고, 굉장히 헷갈리는 상황이 발생하였다. 집중해서 하나의 기능을 구현하는 것이 힘들고 계속 횡설수설 다른 부분을 건드리게 되며 개발 프로세스가 늘어졌다. 다음에 개인 토이 프로젝트를 진행할 때에는 꼭 API 설계 문서를 사전에 작성하리라...
이전에 산학과제로 진행한 프로젝트에서 서버 로그를 확인하다보면 외부에서 보안 취약성을 틈탄 공격패턴들을 여럿 확인할 수 있었다. 정말 단순히 기능만 구현하고, 시각화만 하였던 지난 프로젝트에서 그부분을 보완하지 못한 것이 너무 아쉬웠고, 이번에는 그 기능들에 조금 더 집중해서 구현해보자는 생각에 JWT 생성과 보안 관련된 조치들을 먼저 진행하였다. 근데 API 설계를 미리 꼼꼼하게 설계하지 못한 탓에 계속해서 특정 API에 대한 보안조치를 계속 설정하고 기능 구현하고를 반복하며 속도가 더뎌지게 되었다.
A. 보안 필터
서버에 요청이 들어오면, Controller를 거치기 이전에 보안 필터에서
B. 헤더에 토큰 설정, 쿠키에 토큰 설정
개발을 진행하며 기본 개념이 정말정말 부족하다는 생각과 더불어, Java 공부도 좀더 체계적으로 다시 해야겠다는 생각을 많이 하였다. 이전에 들었던 무료 강의들과 도서들을 다시 살펴보며 개발을 진행하였는데, 이전에 한번 들었던 것이 많이 도움이 됬던 것인지 강의 내용과 관계들이 하나 둘씩 점점 이해되기 시작하였다. 특히 Dto, Dao 등의 개념과 Repository, Service를 왜 나누어야 하는지 약간 의문이 있었는데, 개념을 정리할 수 있었던 기회가 되었다.
A. Service와 Repository의 역할
Repository는 Entity와 직접적으로 소통하는 역할을 하는 것으로 생각된다. 단순히 받아오고, 저장하는 로직들만 담고 있는 것이다. 허나 실제로 비즈니스 로직에서는 유효성 검사 등 여러 과정들이 들어가게 되는데, 이는 Service에서 Repository를 사용해(의존성 주입받음) 비즈니스 로직을 추가하여 기능을 구현하고, Controller에서는 Service만 활용해 요청을 처리하면 되는 것이다.
B. Dto와 Dao
Dao(Data Access Object)와 Dto(Data Transfer Object)는 위의 내용과 계속해 연결된다. Dao는 데이터베이스에서 가까운 부분에서 연결되는, Repository의 역할과 유사하다고 생각하면 되며, Dto는 Repository에서 Serivce, Service에서 또 다른 Service로 이동하며 처리될 때 사용되는 객체들을 의미한다. 위의 사진이 이를 가장 잘 표현해주는 것 같다.
아직 정리되지 않은 것들... (1)
⍰ 프로젝트의 폴더 구조를 어떻게 나누는게 효율적일지 아직도 의문이다... Entity, Serive, Controller, Dto를 각각의 폴더에 나누어 작성하였는데, 뭔가 찜찜한 느낌이다. 또한 굉장히 많은 클래스들을 계속하여 생성해내는 것이 맞는지도 의문이다. 모든 dto들에 대해서 계속해서 작성해주다보면 그 규모가 점점 커질 것 같은데, 이것들은 앞으로 github에서 프로젝트들을 보면서 분석해봐야 할 것 같다.
⍰ Service를 인터페이스와 구현체로 나누어서 작성하는 경우가 여럿 보였는데, 구현체가 또 작성되지 않을 확실한 근거가 있다면 굳이 나누어 작성하지 않아도 된다고 한다. 나는 인터페이스를 따로 만들지 않고 계속 작성하였는데, 인터페이스와 구현체를 나누어 하는 경우가 어떤 경우가 있을지 아직은 와닿지 않는다.
이전 산학과제에서는 MyBatis를 이용해 기능을 구현하였다. 그 당시에도 쿼리를 매번 작성하고, Jackson2와 매핑하는 것들에서 시간이 굉장히 많이 소요되었다.
A. Entity에서 Column 설정 및 테이블 생성
B. 일대일, 일대다 관계 등 매핑
Novel 테이블과 Series 테이블을 분리하며, NovelId를 Series 테이블의 외래키(FK)로 설정해야했다.
아직 정리되지 않은 것들... (2)
⍰ 연관관계를 매핑한 후, 쿼리 자체는 novelId(외래키)를 받아 작성하는게 맞다고 생각해서 input으로 novelId와 Series 객체를 받아 Series 테이블의 novel_id 열에 해당하는 id값과 입력받은 novelId 값을 비교하여 검증하고 데이터를 넣으려 생각하였다. 허나 Entity 클래스에는 실제 객체를 내부 변수로 가지고 있기 때문에 id값을 넣어주니 제대로 매핑되지 않는 문제가 발생해 결국 novelId에 해당하는 Novel을 찾고, 해당 객체를 Series 객체의 Novel 객체로 지정해주는 방식으로 작성하였는데, 아직 개념이 완벽히 정리되지 않은 것 같다. 관련 내용을 좀더 공부하며 예시와 함께 공부하는 것이 좋을 것 같다.
이번 프로젝트는 공부에 많이 초점을 둔 프로젝트였다. 특히 이번에는 각각의 클래스들의 내부를 특히나 많이 들여다 봤다. 어떻게 구현되어있고, 주석의 내용들은 어떤지 많이 확인해보고, 검색도 많이 해보았던 것 같다. 특히 ResponseEntity와 보안필터를 설정할 때 코드를 이해해보려 특히나 노력했다. 이러면서 내가 특히 Java에 대해도 전혀 모르고 있다 뼈저리게 느끼게 되었고, 후에 백기선님의 스터디 영상을 보며 개념들을 정리해나가고자 한다.
첫 프로젝트이다 보니, 개발도 마무리 되지 않았고, 내 스스로의 헛점이 많이 보여 너무나도 아쉬웠지만 배운것들이 정말 많은 경험이였다. 이번 프로젝트를 통해 특히나 문서를 정리하고 시작 전 설계하는 과정이 중요하다는 것을 체감하게 되었다. 또한 빨리 기능들을 구현하고싶은 마음에 동시에 여러 파일들을 계속 오고가며 작성한 탓에 오히려 정리도 안되고 금방 지쳤던 것 같은데, 항상 천천히 하나씩 처리해나가겠다는 마음가짐으로 모든 일을 행해야겠다는 생각이 들었다. 속도를 붙이고 동시에 여러 일을 하는 것은 충분히 익숙해지고 숙련되었을 때 가능하다는 것을 항상 잊지 말아야겠다.