[포스코x코딩온] 웹 풀스택 Node 프로젝트 회고

sima·2023년 9월 24일
0

KDT web-8

목록 보기
11/17
post-thumbnail


프로젝트 명

주차하카

프로젝트 진행 기간

2023.08.31 ~ 2023.09.15

개발환경

Language, Framework - HTML/CSS/Javascript, Node.js
DataBase - MySQL
DevOps - AWS EC2, AWS RDS AWS S3, Docker, Jenkins, Nginx
IDE - IntelliJ, VSCode, MySQL Workbench
Team Coop - Github, ERDCloud, Figma, Notion, Slack
Open API - Kakao Map API, 서울데이터 API, PortOne API

팀 구성

4명(팀장)

프로젝트 URL

http://52.78.196.104/

주제 및 기획의도

자동차 등록대수가 점차 증가하면서 원치않게 주차할 수 없어 돌아다니는 '주차난' 발생
이러한 상황의 해결책으로 비어있는 단독주택, 빌라 거주자 주차공간을 대여하는 '공유주차장' 사업이 만들어짐
하지만 주차공간을 보유한 공급자와 주차장이 필요한 수요자를 간편하게 연결해주는 공유주차 플랫폼은 많이 활성화되어있지 않으며, 정보가 부족한 상태라 판단하여 공급자와 수요자를 이어주고, 타 플랫폼에는 없는 공유주차를 포함한 서울 노상주차장의 사용리뷰를 공유하는 공유주차 플랫폼을 기획

아키텍처 및 설계 산출물

프로젝트 기능 정의


ERD 설계


CI/CD 아키텍처


전체회고

Jenkins를 사용한 CI/CD 파이프라인 구축

프로젝트에서 내가 개발한 기능들을 살펴보기 전에, 별거 없긴 하지만 교육을 들으며 틈틈히 공부하던 CI/CD를 처음으로 실제 팀프로젝트에 적용시켰다. 이전까지는 Git Action과 테스트용 스프링 프로젝트를 기반으로 따로 생성해놓은 서버에서 개인적으로 테스트만 했었는데, 마침 코딩온에서 Node와 서버배포 강의을 동시에 진행하였었다. 다음 프로젝트때 클라우드 서버에 배포를 무조건 할테니, 프로젝트 용 파이프라인을 새로 구축해 사용해보자라는 생각으로 시작하게 되었다.

바로 위에 사진에서 보이듯이, 팀원들의 브랜치에서 메인브랜치로 머지할 때, Github Webhook event를 통해 별개의 서버에서 Docker Container로 띄운 Jenkins에게 hook을 던진다.

파이프라인 코드


정상적으로 hook을 받으면, 작성한 순서에 따라 파이프라인을 빌드하기 시작한다. 먼저 Repository에 방금 머지된 프로젝트를 가져오는데, 이때 스크립트를 통해 전의 프로젝트와 비교해 변경된 파일들을 전부 가져오는 작업을 수행한다.

변경된 파일들 중 package.json이라는 이름을 가진 파일이 있을 경우,(package.json 파일이 변경되었을 경우) 조건문으로 분기를 만들고, 키페어 값을 저장한 환경변수를 이용해 배포서버에 sshagent를 이용하여 원격접속을 한다. 이후 분기에 따라 패키지파일이 변경되었으면 npm install 명령어를 추가적으로 수행하고, 이외에는 pull을 통해 최신버전을 가져와 pm2를 다시 재시작하는 것으로 마무리된다.

프로젝트 기간동안의 파이프라인 빌드 경향

총 89번의 파이프라인 빌드결과를 보면 평균적으로 Jenkins 서버에 프로젝트를 가져오는 데 1~2초, 변경된 파일들을 가져와 원격접속해 프로젝트를 재시작하는데 까지 2초가 걸렸다. 빌드결과의 중간에 원격접속 stage에서 몇분 ~ 몇십분정도 걸리는 빌드가 있긴 했는데 빌드가 실패가 된 적은 없었다.. Console Output을 살펴봤는데 다른 빌드와 딱히 차이점이 없어서 아마 배포서버 원격접속을 하는 스크립트에서 시간을 많이 잡아먹은 것이 아닐까 추측해본다.. 스프링 프로젝트로 CI/CD를 테스트할때는 실행파일을 빌드하는 과정도 있고, 다른 stage들이 있어서 빌드실패를 하는 경우도 있었는데, 비교적 파이프라인이 간단하고 배포서버 접속 이후도 nginx와 pm2를 사용하였기 때문에 빌드실패 없이 무사히 마무리 되었던 것 같다.


서울데이터 API를 이용한 서울시에 있는 노상주차장 데이터 가져와 DB에 저장하기

프로젝트의 주제에 맞게, 전국에 존재하는 모든 노상주차장들의 현황들을 실시간으로 지도에 뿌려야했다. 하지만 전국에 있는 모든 노상주차장 데이터들을 한번에 제공해주는 API는 없었고 각 시,도 에서 제공하는 데이터들을 추합해야 하는 방법밖에 없었다. 추가적으로 우리의 프로젝트는 웹보단 앱에 가까운 기능들을 제공해야 했는데, 실시간으로 데이터를 변경해야 한다는 것이다. 이러한 애로사항들을 단번에 해결해 줄 수 있는 API는 서울 열린데이터 API 였다.

인증키를 받고, 알맞은 요청인자를 넣어 Postman으로 테스트를 해보면
이런식으로 응답을 받을 수 있다. 응답값에는 주소, 주차장이름, 전화번호, 실시간 제공 유무, 총 주차면, 현재 주차면, 현재 주차면 최신 업데이트 시간, 위도, 경도 등 정말 필요했던 정보들을 가져다준다. (이외에도 평일/주말/공휴일 운영시간, 분당 가격 등 정말 주는 정보가 많다...)

서울데이터 API 요청

요청인자를 보면 알겠지만, 일단 응답값으로 받을 수 있는 데이터갯수는 최대 1000개다. 그런데..
제공하는 총 데이터 갯수는 17520개다. 울며 겨자먹기로 18번 돌리니 응답값을 가져오고 DB에 저장하는데만 1분정도 소요가된다. 어찌됬던 DB에 들어온 값들을 확인하고 있었는데 이상한 점을 발견했다.
왼쪽과 오른쪽을 훑어보면 분명 똑같은 데이터로 보인다. 처음엔 왜 같은 데이터가 연속으로 몇십번 들어오지? 라고 생각했는데, 아래쪽에 LATLNG의 값을 보면 살짝 다르다는 걸 알 수 있다. 뭔가 이상하다는 걸 눈치채고 서울데이터 사이트를 뒤적거리 던 중 흥미로운 걸 발견했다.
위에 응답값 중 CAPACITY가 총 주차면갯수, CUR_PARKING이 현재 주차중인 주차면 갯수다. 주차장의 총 주차가능면이 1개인데 어떻게 현재 주차중인 주차면이 10개일까..? 관리자의 답변을 보면 주차면 단위라는 글이 눈에 띈다.
위 사진으로 예를 들면, 출입구가 따로 없이 개방적인 인도 옆 주차칸이 위 상황에 해당될 수 있다. 위 주차칸을 비롯 근처에 있는 모든 개방주차칸이 응답값의 데이터 하나하나이고, 모든 개방주차칸들이 모여 하나의 큰 주차장을 이루기 때문에 이름과 주소는 모두 같고, 위도 경도가 다른 데이터들이 들어오게 되는 것이다.

결론은 내가 알아서 그룹핑을 해야한다.

그룹핑은 객체생성 후 필요한 값 넣기 -> Map과 주차장코드를 이용해 이미 Map객체가 가지고 있으면 객체의 총 주차면수 + 1를 하여 값 넣기, 가지고 있지 않으면 그대로 넣기 -> 모든 주차장 데이터를 가지고있는 Map 객체를 반복하여 DB에 INSERT 순서로 진행하였다.
위 과정을 거치니 개발당시 기준 17511개의 데이터를 202개로 줄일 수 있었다.


node-schedule 모듈을 이용해 데이터 최신화

이제 가져온 데이터들의 실시간 현황을 업데이트해야한다. Spring엔 Scheduler가 있듯이 Node 역시 node-schedule 이란 모듈이 있다.

node-schedule은 cron 표현식을 사용해 지정한 시간에 함수를 실행한다.

*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    │
│    │    │    │    │    └ 요일 단위 (0 - 7) 
│    │    │    │    └───── 달 단위 (1 - 12)
│    │    │    └────────── 일 단위 (1 - 31)
│    │    └─────────────── 시 단위(0 ~ 23)
│    └──────────────────── 분 단위(0 ~ 59)
└───────────────────────── 초 단위(0 ~ 59)

위 표현식을 참고하면, schedule.scheduleJob('10 * * * * *')... 은 10초마다 콜백함수를 실행시킨다. API의 응답값중 `CUR_PARKING_TIME 은 가장 최근 업데이트 된 시간을 제공하는데, 계속 API를 요청해 확인해보니 5~6분 간격으로 업데이트가 되는 것을 확인할 수 있었다. 그래서 3분 간격으로 함수를 실행시키기 위해 scheduleJob('* 3 * * * *')로 작성하고 테스트해봤는데, 실행이 되지 않아 레퍼런스를 찾아보던 중 내가 작성한 코드는 매 시 3분을 표현한 것이었다. 다른 사람들이 사용한 코드들을 참고하여 scheduleJob('0 0/3 * * * *') 으로 바꾸니 원하던 대로 3분마다 콜백함수를 실행시키는 것을 확인할 수 있었다.

전체적인 순서는 먼저 DB에서 저장된 값중 1개를 가져오는데, curparkingtime 컬럼만 가져오고 questatus의 값이 1인 것 하나만 가져온다. 그리고 API를 요청해 역시 1개의 응답값 중 CUR_PARKING_TIME 만 가져온다.

각각 가져온 두 개의 값들 Date형식으로 파싱하여 비교하여, API에서 받은 날짜값이 더 최신이라면 다시 17000여개의 데이터들을 요청한다. 초기 데이터를 저장하는것과 달리 데이터들을 탐색하는 과정에서 실시간 데이터 제공유무를 알려주는 QUE_STATUS 값이 0인 경우(실시간 정보 제공 X), Map 객체에 이미 해당 주차장 객체값이 있는 경우 Map에 추가하지 않는다. 이후 Map 객체를 이용하여 값들을 업데이트해주는 것으로 마무리된다.


KAKAO MAP API를 사용하여 지도에 오버레이 띄우기

카카오 맵 API를 사용하면 위 사진처럼 원하는 위치에 마커를 표시할 수 있다. 우리 프로젝트는 메인에서 바로 노상주차장의 주차면수 현황, 공유주차장의 가격을 보여줄 것이기 때문에 마커가 아닌 커스텀 오버레이로 표시를 진행하였다.

mapContainermapOption을 통해 지도 확대크기, 처음 위치인 중심좌표를 설정하여 카카오 맵을 생성한다. ps 변수는 나중에 검색한 위치를 기준으로 지도위치를 변경해주기 위해 미리 선언해두었다. mapOption 객체 중 center 값이 초기 지도의 위치를 인자값으로 위도, 경도를 받아 설정하는데, 개발할 당시에는 Geolocation 모듈을 사용해 현재 위치값의 위도, 경도값을 받아와 사용하였다.. 그러나 중간에 배포서버에서 테스트해보니 모듈이 제대로 작동하지 않았다. 이 내용은 뒤에서 설명하도록 하겠다.
메인페이지가 렌더링될 때, 지도에 바로 주차장 데이터들을 뿌려줘야 하기 때문에 즉시실행 함수로 API를 요청해 데이터를 가져와 result 변수에 담아둔다.

커스텀 오버레이를 띄우기 위해선 예시로 제공되는 contentlatlng 필드를 사용해야 한다. content는 실제 커스텀 오버레이의 모양을 잡아주고, latlng는 위도 경도값으로 어느 위치에 띄울지 결정한다.
가져온 모든 주차장 데이터들을 알맞은 위치에 오버레이를 띄울 것이기 때문에, publicParkingPositions 이라는 빈 배열을 선언하고, 주차장 데이터들을 탐색하며 필요한 값(총 주차면수, 현재 주차중인 면 수, 위도, 경도)를 사용해 content, latlng 를 가지고 있는 객체형식으로 배열에 넣어주고, 커스텀 오버레이를 생성한다.이런 모양으로 실제 노상주차장이 있는 위치에 커스텀 오버레이가 찍히는 걸 확인할 수 있다.

Geolocation module 사용

위 작성한 내용 중 메인페이지에 렌더링될 때, Geolocation 모듈을 사용해 현재 접속한 위치를 파악하고, 위치의 위도 경도를 기준으로 카카오 지도의 첫 시점을 잡아주는 역할을 할 예정이였다. 우리의 프로젝트는 물론 미리 목적지에 가기전에 사용하여 주차장을 알아볼 수 있지만, 보통의 경우엔 평소 운전을 하다 목적지 주차장이 다 만석인 경우와 같이 바로바로 사용해야 하는 경우가 대부분일 것이다. 그래서 실시간 위치정보를 제공해주는 모듈을 사용해 로컬에서 해당 기능을 구현하고 배포 후 테스트를 해봤다.

그런데 개발당시에는 현재 위치 기준으로 지도가 떴었는데, 배포서버에서는 지도 자체가 생성되지 않았다. 처음엔 카카오 지도 API가 문제인거 같아 코드를 찾아봤는데도 문제점이 보이진 않았다. 그러던 와중..
공식 문서를 살펴보니 Geolocation API는 https 인 프로토콜에서만 지원한다는 것을 발견할 수 있었다. 로컬에서는 정상적으로 지원이 되지만, 배포서버에 사용한 AWS RDS는 웹 브라우저 프로토콜이 http이기 때문에 아예 사용할 수 없게 된 것이다.
이에 대한 피드백으로 리더님이 https를 사용하는 서버에 배포하는 다른 클라우드서버를 알려주셨지만, 해당 문제점을 발견할 땐 이미 프로젝트 마감이 얼마 남지않았던 시점이였기 때문에 아쉽지만 카카오 지도 생성시 위도와 경도를 시연할 때 기준으로 고정값을 사용할 수 밖에 없었다.

노상주차장 정보 상세보기

생성된 커스텀 오버레이를 클릭하면 해당하는 주차장의 상세페이지로 이동한다. 메인에서는 현재 주차면 현황만 보여줬지만, 상세로 들어가게 되면 유료/무료 여부, 가격, 운영시간, 리뷰, 평점 정보를 확인할 수 있다.

리뷰작성 버튼을 누르면 간단하게 평점과 코멘트를 해당 주차장의 사용리뷰로 남길 수 있다.

공유주차장 등록

메뉴바를 클릭하면, 로그인 정보와 공유주차를 관리할 수 있다.

공유주차장 등록에 들어오게 되면, 주차공간 이름, 주소, 가격, 사진을 정보로 받으며, 주소같은 경우는 카카오 우편번호 API를 사용하여 주소를 받는다. 등록한 공유주차장 역시 메인의 지도에 오버레이를 띄워야 하기 때문에 위도와 경도가 필요하는데, 마침 해당 API에서 입력한 주소지에 해당하는 위도와 경도를 값으로 제공한다.

사용자가 검색한 주소값을 받아오고, 위도와 경도를 담아 최종적으로 공유주차장 등록을 할 때 DB에 같이 저장한다.

후기

일단 마지막날 제외 15일이라는 짧은 기간동안 생각보다 꽤 많은것들을 시도해봤던 것 같다.
이전에 Spring으로 MVC패턴을 이용한 프로젝트를 몇개 해봐서 express에서의 MVC패턴을 수월하게 이해할 수 있었지만, 교육과 실습만으로는 머리속에서 떠오르는 대로 코드를 바로 작성하기 어려웠는데 프로젝트를 진행하면서 자연스레 해소된 것 같다. Sequelize ORM도 마찬가지로 join이나 여러 조건들을 사용해 어느정도 머리속에 들어오고 손에 익은 것 같다.

전번에 진행했던 프로젝트같은 경우는 프론트엔드로만 이루어져 있고, 각자 맡은 페이지들을 구현했기 때문에 바로 메인에 머지해도 충돌날 일이 없었다. 그러나 이번에 진행한 프로젝트 같은 경우엔 기능을 기준으로 역할을 분리했다 하더라도 뷰 쪽에는 상당히 많은 충돌이 일어났다.(특히 메인페이지에서 엄청났다) 다른 팀원들은 VSCode를 사용해 작업했지만, 나는 교육초반부터 IntelliJ를 사용하여 진행했기 때문에, 자동으로 생성되는 파일들을 모두 .gitignore에 넣었어야 했었다.
그리고 어떤 이유에서인진 모르겠지만.. VSCode에서 프리티어로 자동 줄바꿈이 되고, IntelliJ를 사용한 나는 플러그인을 사용하지 않았었는데 같은 내용의 코드임에도 불구하고 Git에서 줄바꿈으로 다른 코드로 인식을 하는지 충돌이라 판단하지 않아 뷰쪽에서 같은 코드가 중복된 채로 머지가 되는일이 잦았다. 이거때문에 정말 시간을 많이 날려먹었던게 좀 아쉬웠다.

마지막으로 개인적으로 Git Action이나 Jenkins로 CI/CD를 공부하면서, 무엇인가를 처음부터 혼자 관심을 가져서 실제로 적용시켜 보는게 정말 오랜만인 것 같다. 혼자 테스트를 해보면서 제대로 사용해볼 수나 있을까 막막했는데 생각외로 잘 작동해서 정말 다행이었고, 작게나마 팀프로젝트에서 중간중간 배포를 하는 번거로움을 없애주고 문제점들을 빨리 찾을 수 있어서 뿌듯했다.

0개의 댓글