두 번째 프로젝트 회고
이번 글에서는 교육원 수료 후에 스터디 모임(사실상 파이널 조원중에서 수료 후에 스터디 더 할분들 모집한 모임)에서
진행한 프로젝트에 관한 해프닝, 구현기능 및 코드 정리, 느낀 점, 아쉬운 점을 기록해보려 한다.
이번 프로젝트는 나를 포함한 총 3명의 백엔드 팀원과 함께 진행하였고, 그동안 학습한 내용과 더불어 추가로 구현해보고 싶었던 기능들을 적용해 보기 좋은 주제를 상의하다 북코아라는 중고도서판매 사이트를 벤치마킹해 만들어보기로 결정했다.
1. 무언가 잘못된 파트분배
첫 번째 회의에서 파트분배를 어떤 식으로 할지 먼저 정해 보기로 했는데 기능별로 맡을지, 페이지별로 담당할지 고민했었다. 이전 프로젝트에서는 기능별로 파트를 분배해서 개발을 진행했었는데 이런 식으로 진행했을 때는 페이지당 개발 부담감은 줄었지만 팀원 간 공통적으로 사용해야 하는 부분이 있을 때나 병합을 진행해야 할 때 겹치는 부분이 많아 오류 수정에 많은 시간이 들어갔었다.
이런 경험과 함께 이번엔 다른 방식으로 협업을 진행해보고 싶어서, 각자 페이지 전체를 맡아 개발을 진행하기로 결정했다. 그렇게 로그인 페이지 담당, 메인페이지 담당, 상세페이지 담당을 우선적으로 나누었고 추가적으로 개발해야 할 페이지들을 중요도 순으로 정리하였다.
나는 우선 상세페이지와 기능을 담당했는데, 등록된 도서의 상세 내용을 보여주고 부가적으로 사용자가 해당 도서를 원하는 수량만큼 장바구니에 추가할 수 있게(물론 남은 재고가 있을 시에만) 하면 되는 페이지였다. 그런데 이번 프로젝트를 통해서 구현해보고 싶은 기능이 있었다. 바로 결제기능이었는데, 이전 프로젝트에서 사용해보지 못했던 Open API를 사용해보고 싶었기 때문이었다. 그래서 결제기능 까진 내가 해보고 싶다고 팀원들한테 이야기했는데, 여기서부터 문제가 생겼다. 앞서 페이지 별로 파트를 맡아서 그 페이지에 속한 기능까지 개발하는 방식을 채택했기 때문에 결제기능을 구현해 보려면 그와 관련된 페이지까지 통으로 맡아야 했다. 그렇게...... 여차저차 내가 맡은 페이지는 도서 상세페이지, 장바구니, 보관함, 결제페이지까지 이렇게 왕창 담당하게 되었다.
파트분배를 하고 나서 "내가 이걸 다 할 수 있을까?"라는 생각도 들었으나 교육원 프로젝트 때처럼 무조건 정해진 기간 안에 완성해야 하는 상황은 아니었기에 "오히려 좋아..."라는 마음으로... 일단 다들 취업준비 일정도 있고 하니 한 달의 개발기간을 잡고 열심히 해보기로 했다.
2. 잘 가라 무결성 오류!
어김없이 다음 순서는 DB설계! 이전 프로젝트에서는 뼈아픈 경험을 했었기 때문에 지인 개발자분의 조언도 얻어가며 꼼꼼하게 설계했다.
파이널 프로젝트에서 가장 많이 접한 에러 내용은 단연 무결성 제약 조건 오류와 NullPointException이었을 것이다.
때문에 이번엔 DB 모델링에 관해 조사를 진행한 다음 설계를 진행하였다.
조사를 진행하면서 새롭게 알게 된 것은 현업에서는 외래키를 잘 사용하지 않는다는 것이었다. 지금까지는 관계되어 있는 테이블은 외래키로 이어주는 것이 당연한 것인 줄 알고 있었지만, 제약조건이 있으면 데이터 수정이 까다로워지기 때문에 개발 단계에서의 편의를 위해 잘 사용하지 않는 경우가 많다는 걸 알게 되었다. 추가적으로는 데이터 Insert나 Update시에 무결성 검사의 선행은 성능 저하를 일으킨다는 이유도 있었다.
그렇게 서버 단에서 무결성을 검사할 코드를 추가하기로 하고 DB는 전체적으로 위의 이미지처럼 테이블을 생성하였다.
결과적으로 서버단 코드에 좀 더 신경을 써줘야 했기에 전체적인 개발 속도는 엄청 크게 차이가 난 것은 아니었지만, 제약조건 위배 오류와 널 포인트 익셉션 오류에서 대부분 해방될 수 있었기 때문에 개발단계 테스트 수행 시에 수고로움을 줄일 수 있었다.(스트레스도 줄었던 건 부가요소...)
3. 개발시작
파트분배와 DB설개가 끝나고 본격적인 개발에 착수...
파트분배를 페이지 단위로 나누고 나니 개개인의 개발 부담은 늘어났지만, 파트가 겹칠일이 없어져 상당히 편하게 진행할 수 있었다. 개발할 양이 만만치 않았기에 부지런히 개발에 임했다.
1) 상세페이지
상세페이지에선 기본적으로 메인페이지에서 관리자가 등록한 도서 정보를 DB에서 불러와 사용자 화면에 띄워주는 게 대부분인 파트이다. 부가적으론 최신등록도서 목록과 남은 재고에 한해서 원하는 수량만큼 상품을 장바구니에 추가시키는 기능이 있다. 하나씩 살펴보면...
단순히 불러온 데이터 외에 내비게이션 바나 footer부분은 다른 페이지에서도 공통으로 사용되기 때문에 따로 모듈화 시켜서 사용하였고, 최신 등록도서는 관리자가 등록하는 도서 테이블의 1~6번째 Row에 해당하는 값만 불러오도록 해서 새로 등록될 때마다 최신화될 수 있도록 구현했다.
마지막으로 구매자가 원하는 수량만큼 장바구니에 추가할 수 있게 하는 기능은
1차적으로 로그인이 되어있는지 확인하고 해당 계정의 장바구니 테이블에 추가하려는 상품이 이미 있는지 중복체크를 2차적으로 확인하며, 로그인과 중복체크 충족시에 Insert작업을 진행하는 방식으로 구현하였다.
수량은 구매자가 설정한 수량에 맞춰 가격이 계산될 수 있게 이벤트를 설정해 주었다. 처음 구현했을 때는 바보 같은 실수를 했었는데, 수량조절 버튼에 이벤트를 걸고 가격계산 함수도 만들어 뒀으면서 수량버튼에 가격계산 함수를 걸지 않아서 계산이 되지 않았다. 간단한 문제였는데 이것 가지고 한참을 고민했던 기억이 난다...
사용자가 원하는 수량으로 장바구니 추가버튼을 누르면 해당 도서에 대한 정보를 @RequestBody를 이용하여 받아오고 DB에 Insert 한다.
2) 장바구니
장바구니에는 기본적으로 등록된 리스트를 불러오고 각각에 상품에 대한 수량조절, 가격계산, 선택상품에 대한 보관함 추가 및 배달비용을 포함한 가격계산 마지막으로 상품삭제기능이 있다.
장바구니는 시작 전에는 간단할 것 같은 페이지였으나, 생각보다 구현에 신경 써야 하는 부분이 많아 시간이 좀 걸렸던 파트이다. 기능을 살펴보자면
우선 메인페이지와 상세페이지에서 사용자가 장바구니 추가를 하면 장바구니에 해당 상품이 추가된다.
그러면 이미지처럼 장바구니 페이지에 추가된 상품리스트를 불러오고, 상세페이지에서와 마찬가지로 각각의 상품에 대한 구매 수량조절과 판매가 자동 계산 기능이 제공된다. 판매가에서 위에 있는 가격이 정가이고 아래 있는 가격이 할인가격이다.
상세페이지의 수량조절과 구현에 있어 다른 부분은 장바구니에선 상품이 여러 개다 보니 상품 관련 기능에서 index를 명확하게 지정해 주어야 오류 없이 구현이 가능했다.
오른쪽에 있는 주문 금액 폼은 편의를 위해 사용자 화면이동에 맞춰서 따라오게 구현하였고 체크한 상품의 총 결제금액을 계산해서 보여준다.
나머지 기능은 버튼 기능인데 위쪽의 본관함 아이콘을 클릭하면 보관함 페이지로 이동, 선택상품 삭제는 복수삭제에 편리하고, 단일삭제는 각상품 별로 지원하는 삭제버튼을 클릭하면 삭제된다. 마지막으로 보관함 버튼을 누르면 해당 상품이 보관함 페이지로 이동한다.
마지막으로 주문 금액 폼의 주문하기 버튼은 구매를 원하는 상품을 체크한 뒤에 클릭하면 결제페이지로 상품정보와 함께 이동한다.
3) 보관함
보관함은 구매자가 구매할지 말지 고민되지만 장바구니에선 정리하고 싶은 상품을 따로 보관하는 용도의 페이지로 기능은 장바구니와 매우 흡사하다. 다른 부분은 보관함에서는 구매가격만 확인가능하고 따로 구매는 불가능하다.
구매를 결정했으면 상품을 클릭해서 상세페이지로 이동한 다음 거기에서 장바구니에 다시 추가하면 된다. 삭제기능은 장바구니와 동일하다.
보관함은 장바구니와 유사하고 상품데이터만 잘 불러오고 보내면돼서 크게 어려운 부분은 없었던 것 같다. 반대로 장바구니에 시간이 많이 들어갔었다...
4) 결제페이지
대망의 결제페이지다.
처음으로 주소와 결제에 필요한 Open API를 사용해 볼 수 있었고 추가적으로 마일리지 기능도 구현해 볼 수 있었다.
주문상품 부분은 앞서있던 장바구니와 보관함 파트의 리스트와 대부분 비슷하지만 구현은 가장 어려웠다.
장바구니에서 선택 상품의 정보와 수량이 순서에 맞게 넘어오지 않았기 때문이었는데, 구매상품 정보를 가져오려면 최종적으로는 각각의 상품 정보를 JSONArray 객체에 담아서 구분된 배열의 형태로 결제페이로 보내주어야 했는데, 이형태를 만드는 것이 많이 어려웠다. 상품별로 구분 없이 정보가 담겨서 리스트 순서도 안 맞고 리스트 순서가 맞아도 그 상품에 대해 구매자가 정한 수량의 순서가 따로 놀기도 했다.
결과적으로는 위 코드처럼 구현하여 해결하였지만 장바구니에서부터 데이터 형태를 염두해두고 만들 수 있었다면 이것보다 단순하고 깔끔한 코드로 구현할 수 있지 않았을까 하는 생각도 든다. 배열로 가져온 데이터를 Map인터페이스에 담고 그걸 또 리스트에 담고 이걸로도 수량은 해결이 되지 않아서 json객체에 담을 때 수량은 따로 반복문을 이용하여 결제페이지에서 꺼내 사용할 수 있게끔 하여 해결하였다. 지금도 그렇지만 나는 데이터 형태를 만드는 것이 제일 어려운 것 같다...
다음으로는 간단하게 주문자 정보를 받는 부분이다. 구매자에게 입력값을 받아와서 결제가 이루어지면 결제정보 테이블에 저장되는 형식이다. 여기서 카카오 주소 API를 사용해 보았는데 코드가 굉장히 사용하기 편하게 잘 정리되어있어서 간단하게 구현할 수 있었다. 입력 정보는 사용자 편의를 생각하여 회원가입 할 때 입력했던 정보가 기본으로 들어가 있고 배송지나 기타 정보를 변경해서 구매하고 싶다면 '새로운 주소'탭을 클릭하여 정보를 입력해 주면 된다.
결제기능은 아임포트를 통한 이니시스결제를 구현하였고 결제화면은 다음과 같다.
정상적으로 결제가 이루어지면 결제완료 알림을 띄워주고 결제완료 페이지로 이동하도록 구현하였고, 결제정보가 DB에 저장된다.
마지막으로 결제시에 사용할 수 있는 마일리지는 구매를 하게되면 결제된 금액에 따라서 자동 적립이 되며 자신이 보유하고 있는 마일리지는 결제가격을 넘지 않는 선에서 자유롭게 사용할 수 있다.
마일리지는 결제기능을 만들면서 부가적으로 추가한 기능이었는데 결제기능에 마일리지를 추가로 구현해 보면서 크게 느낀점은 백엔드 개발에 있어서 더욱 중요한 것은 기능자체보다 그 기능이 동작한 후의 처리가 꼼꼼하게 이루어져야 한다는 것이었다.
결제기능 자체는 아임포트에서 지원하는 API가 어떻게 작동하는지만 이해하면 약간의 시간투자만으로 구현이 가능하지만 완벽한 기능이되려면 구매자가 결제를 진행한 후의 서비스로직을 정확하고 꼼꼼하게 작성해야주어야 한다. 예시로 마일리지와 재고 정도를 들 수 있을 것같은데, 결제 후의 상황이나 결제가 취소됐을 상황에 맞춰서 마일리지가 추가되고 빠지고, 재고가 추가되고 빠지고하는 과정을 자동화시켜주는 로직을 만들어두어야만 결제기능이라 부를만한 모양세가 만들어진다.
4. 느낀 점
교육원에서 생긴 인연이 운 좋게 수료 후에도 이어져 이렇게 알찬 프로젝트를 경험해 볼 수 있음에 감사하고 싶다.
파이널프로젝트 경험만으로 취업전선에 바로 뛰어들기에는 심적으로나 포트폴리오 상으로나 너무나 불안했는데 제안해 주고 함께해 준 팀원들 덕분에 불안감이 어느 정도 해소된 것 같다.(물론 아직도 많이 부족하지만 말이다 ㅎㅎ;;;)
프로젝트를 시작하고 메인파트는 거진 다 맡아버려서 부담감이 컸지만, 마무리하고 보니 오히려 좋은 경험이었다.
처음 구현해 보는 기능과 기술에 많이 어렵고, 힘들기도 했지만 역시 성장하려면 부딪혀 보는 것이 최고라는 것을 다시금 느낄 수 있었다. 부담스럽고 어렵다고 도전도 안 해보면 영원히 모를 것이 아닌가? 본격적인 취업준비 이전에 이것저것 시도 해봐서 다행이라고 생각한다.
이번 개발을 진행하면서 자주 느꼈던 것은 데이터 구조를 원하는 형태로 만드는 능력이 아직 많이 부족하다는 것이다. 또 좋은 코드라곤 할 수 없지만 다중 for문을 사용할 때도 조금만 복잡해지면 결과의 형태가 잘 그려지지 않았다. 이런 부분은 많이 만들고 접하다 보면 자연스럽게 인지가 가능해진다는 조언을 듣긴 했지만, 데이터를 넘기거나 받을 때 원하는 형태로 만들지 못해서 한참 동안 작업이 막히는 경우가 다분하니 스스로 답답해지기도 하고, 빨리 잘하고 싶은 맘에 더 초초해지기도 했던 것 같다. 솔직히 아직까지 어떤 방식으로 공부해 나아가야 부족한 점을 빠르게 학습하고 발전시킬 수 있을지 잘 모르겠고 막연하기만 하니 우선 현업선배? 분들이 해보라는 것은 어지간하면 일단 다 시도해 보며 공부해 볼 생각이다...(가령 이렇게 기술블로그를 시작한 것처럼...)
마지막으로 이번 프로젝트도 함께해 준 팀원들에게 감사드리며 다들 좋은 경험이었길 바라고, 아직도 많이 부족하지만 열심히 학습해서 모두모두 좋은 곳에 취업하기를 바라본다.
5. 아쉬운 점
프로젝트를 마무리하고 느꼈던 아쉬운점을 간단하게 정리해보려 한다.
1. 스크립트 단에서 JS와 jQuery를 잘 구분해 사용하지 못한 점
위에 정리해 둔 코드만 봐도 보이지만 기능구현을 하고 보니 바닐라자바스크립 와 제이쿼리를 섞어서 사용한 경우가 있었다. 원인은 급하게 개발한 것도 있겠지만, 확실한 건 내가 바닐라 JS와 jQuery 둘 중 어느 것 하나 명확하게 사용하지 못하기 때문일 것이다.
깔끔하고 남들도 보기 좋은 코드가 되려면 방식도 중요하지만 통일된 언어를 사용해야 한다는데, 결과물을 보니 깔끔하지못해 많이 아쉬웠었다. 앞으로 프론트 학습도 좀 더 신경 써서 해야 할 것 같다...ㅠㅠ
2. 환불기능을 구현하지 못한 것
결제 기능을 구현하기로 했으니 당연히 환불기능까지 개발 계획에 잡혀있었는데... 결과적으로 끝끝내 개발하지 못하고 마무리 됐다. 맡은 파트가 많아 시간이 부족한 부분이 가장 컸고, 결제기능과 비교해서 개발난도가 높다 느껴졌었다.
결제 API에 비해서 환불 API는 이해하기가 어려웠고, 로직 적용도 잘되지 않았다. 그렇게 얼마 안남은 개발기간에 고군분투만 하다가 마지막 병합날까지 완성시키지 못하고 진행하게 되었다. 처음 적용해 보는 API였는데 생각보다 어렵기도 해서 앞으로가 걱정되기도 했고 멋지게 완성해보고 싶었는데 애매하게 끝나서 끝이 찝찝했다...
3. 초기 만들기로 했던 파트들을 다 만들지 못한 것
시간이 부족할 것은 이미 예상하여 개발 후순위로 남겨뒀던 몇몇 파트들이 있었다. 마이페이지 파트와 1:1문의 파트였는데, 이 부분은 각자 맡은 부분이 빨리 끝나는 팀원이 조금씩 나눠서 맡기로 하고 개발에 들어갔으나 나도 그렇고 다른 팀원들도 이것저것 준비할게 많아서인지 각자파트를 마무리하는 데에도 프로젝트 기간이 너무나 짧았다. 나는 하나를 시작하면 어지간하면 끝을 보고 다음으로 넘어가는 성격인데 뭔가 이번 프로젝트는 기능적으로 많이 도전해 본 부분은 매우 흡족하지만, 목표했던 것들을 전부 완성시키진 못하고 끝이나 아쉬움이 큰 프로젝트인 것 같다.
4. 모듈화를 잘하지 못하였다.
이 부분도 개발을 어느 정도 끝내고 마무리 단계에서 내가 만든 코드들을 살펴보다 발견한 것이었는데, 페이지별로 중복된 코드들이 상당히 많이 보였다. 넓은 시야로 개발을 진행하지 못하고 기능 하나하나에만 신경 쓰면서 개발하다 보니 이런 문제가 발생한 것 같다. 중복된 코드는 줄이고 자주 사용되는 메서드나 기능들은 따로 모듈화를 시켜서 호출해서 사용하는 게 좋다고 배웠는데 실제 적용을 잘 못했던 것 같다. 학습량이 부족하다 보니 실제로 적용하기도 쉽지는 않을 것 같지만... 아무튼 조금이라도 더 깔끔하고 보기 좋은 코드를 만들 수 있었는데 현재 코드들은 지저분해 보여서 기분이 썩 좋지 않은 것 같다. 만약 다음프로젝트를 추가로 더 하게 된다면 제발 이 글 한번 더 읽고 실수하지 않았으면 좋겠다...
5. service 로직을 controller에도 작성한 것
위의 코드 이미지를 보면 알 수 있겠지만 교육원 수업 스타일의 여파였을까 저런 방식에 익숙해져 있었던 나는 요청과 응답만 담당해야하는 controller단에 service로직까지 섞어 사용하는 좋지 못한 습관이 있었다. 프로젝트가 끝나고 지인분한테 피드백을 받고 좀 더 공부하면서 알게된 부분이었지만 지금생각하면 처음부터 이상한 습관을 들인 것에 대해 안타까웠다.다음 프로젝트 부터는 정말 많이 신경써야할 것 같다...
이상으로 역시나 힘들었지만 너무나 알찼던 나의 두 번째 프로젝트 회고를 마무리 한다.
글제주가 없어서 엄청 못썼는데 긴 글 읽어주신 분들이 계시다면 정말 감사드립니다!
노션포트폴리오 링크
깃허브 링크