개발일지

실전 프로젝트

오늘도개발 2024. 10. 21. 10:01

* 2024.04 ~ 2024.05 (6주) 실전 프로젝트 한 것 총 정리

 

 실전 프로젝트 개요

 

 이번 프로젝트는 백엔드 프론트엔드로 나뉘어 각자의 역할에 맞는 기능을 구현하는 실제 서비스팀의 업무와 유사하게 진행되었습니다. 프로젝트는 6주 동안 기획, 설계, 구현, 유저 테스트, 발표를 모두 진행해야 했기 때문에 시간이 많이 부족하였습니다. 그래서 Back-end Leader로서 프로젝트를 성공적으로 완료하고 정해진 일정(6주) 내에 핵심 기능을 모두 구현하기 위해 노력하였습니다.

 

* 팀 구성은 Design 1명, FE 2명, BE 4명으로 구성되었고 BE 개발자는 다음과 같이 역할 분담을 하였습니다.

 

팀원1 : 여행 후기 CRUD, 이미지 처리, 좋아요, 스크랩, 모니터링 툴 도입(Grafana, Promethus)

팀원2 : 여행 플랜 CRUD, 투표 기능 , CI/CD, 좋아요, 스크랩

팀원3 : 로그인, 회원가입, 알림 기능

본인 : 랭킹 기능, 검색 기능, 추천 기능, 성능 분석 , 좋아요, 스크랩, 테스트 툴 도입(JMeter, Java Faker)

 

 

 목차 ( 클릭시 해당 위치 이동)

1. 프로젝트 소개

 

2. 시스템 아키텍쳐 설계 및 기술 선택 과정

 

3. 조회 성능 개선

   - 목록 조회 속도 개선 ( offset -> cursor 기반)

   - 상세 페이지 조회 개선 ( Global Cash 사용 )

   - Main 페이지 조회 속도 개선 ( Rank Top 10 기능 관련_ sorted sets 자료 구조 적용 )  

 

4. 검색 속도 및 기능 개선(Elasticsearch)

   - 검색 성능 개선 ( Full Text Search(Mysql) vs Elasticsearch )

   - 사용자 검색 로그 기능 추가

   - 한글 관련 검색 편의 기능 추가( 초성검색 / 영타-한글 변환 / 한타-영어 변환)

 

5. 게시글(여행후기) 추천 기능 (Challenge)

   - 좋아요 기반 추천 기능  ( apriori 간소화 알고리즘 )

   - 게시글 내용 기반 추천 기능 (BM 25 알고리즘)

   - batch 적용 및 최적화를 통한 추천 기능 속도 개선 ( chunk 기반 병렬처리, Redis Pipeline 요청 )

 

6. 사용자 Feedback

 

 

 1. 실전 프로젝트 소개

 

  어떻게 여행할지 계획이 힘들다구요?! 플랜을 여러개 작성해서 투표하고 다녀오세요~!

     물론?! 후기도 남기면 함께 여행가는 즐거움을 느낄 수 있습니다!

 

   * 빠르고 쉬운 플랜 및 후기 작성을 바탕으로 맞춤 여행 플랜 및 후기 정보를 제공합니다.

 

🅶 Back-End Github →

 

GitHub - TravelLand/Travly_BE: 떠나볼까 BE repository

떠나볼까 BE repository. Contribute to TravelLand/Travly_BE development by creating an account on GitHub.

github.com

 

 

  주요 기능

 

1. 여행 후기

 

- 국내 다양한 여행지를 추천하고, 다양한 경험을 공유하며 새로운 여행 목적지를 발견할 수 있도록 도와주는 공간

 

 

2. 여행 플랜 및 투표

 

 

 - 개인 또는 친구들과 함께 통하여 계획을 세워 볼 수 있습니다.

 - 만약 일정이 여러개 라면 투표를 통하여 의견을 수렴 할 수 있습니다.

 - 새로운 여행지에 대한 계획을 하고, 먼저 다녀온 사람들의 의견을 들어볼 수 있는 공간

 

 

3. 기타 편의 기능

- 검색, 좋아요, 스크랩, 인기검색어 추천 기능을 제공합니다.

 

 

 

 2. 시스템 아키텍쳐 설계 및 기술 선택 과정

 

 시스템 아키텍쳐는 서비스의 규모를 확장 할 수 있는 형태로 설계하였고, 기술 스택은 대중적으로 많이 사용하는 가, 이전에 교육 받은 내용에 포함되는 가, 레퍼런스 자료가 많은 가를 기본적으로 고려하면서 선택하였다.

 

 또한, 요구하는 기능에 적합한 기술을 선택하기 위해 노력하였고, 사이드 이펙트를 최소화 하기 위해서 Spring-boot에 일치하는 버전 사용 및 무분별한 인프라 도입은 지양 하였다. 

 

 

 

  아키텍쳐 설계 과정

 

  - Spring-boot로 개발을 진행하므로 MVC 패턴과 OOP 위주 설계

 

  - OOP 및 데이터간 관계성을 위해서 RDBMS를 선택(MySQL)

 

  - RDBMS 는 Scale out 에 상대적으로 고비용이 든다는 단점이 존재함으로 별도의 Cache를 사용하고(Redis) 관리하여 DB의 부하를 줄임

 

  - 다양한 한글 검색 기능과 DB의 부하를 줄이기 위해 Elasticsearch 도입

 

  - AWS 인프라 사용 (EC2, Elastic Cache, S3)

 

  기술 선택  과정

 

1. IntelliJ  VS Eclipse

- 사용 편의성 고려

- 백엔드 팀원 모두 익숙한 tool 선택

- 제공 받은 기간제 유료 버전 사용

 

2. AWS  VS  GCP  VS  NCP

- 레퍼런스가 많음

- 다양한 인프라를 함께 제공

- 높은 안정성

 

3. Redis  VS  memcached

- Global cache

- 다양한 옵션을 지정하여 디스크에 저장가능

- 다양한 자료구조 제공

 

4. MySQL  VS  Oracle DB

- 레퍼런스가 많음

- 오픈소스

- 지속적인 버전업으로 성능개선

 

5. Elasticsearch  VS  MySQL Full text search

- 역인덱스 구조로 검색 효율화

- 다양한 한글 관련 플러그인 지원

 

 

 3. 조회 성능 개선

 트러블슈팅 대상은 문제 발생 가능성이 높은 부분을 중심으로 확인하였고, 실제 성능 저하가 발생한 경우 원인을 분석하였다. 원인 분석 후에는 새로운 기술이나 코드 구현을 통해 서비스에 적합한 방식으로 문제를 해결하기 위해 노력하였다.

 

 또한, 과도하게 문제를 찾아내어 오버 엔지니어링하는 실수를 방지하기 위해, 예상 서비스 범위(게시글 50만 건, 유저 10만 명, 동시 요청 10건)를 고려한 테스트를 지속적으로 수행하며 트러블슈팅을 진행하였다.

 

 

   목록 조회 속도 개선 ( Cursor 기반 및 쿼리문 개선)

 

 Offset 기반 페이징 처리는 Offset 숫자가 커질수록 해당 위치를 찾기 위해 불필요한 카운팅이 발생한다. 이를 해결하기 위해, 이전 위치를 기준으로 Index를 통해 바로 조회하는 Cursor 기반 페이징으로 리팩토링 작업을 진행하였다.

 

 

 

 또한, JPA의 기본 메서드로 페이징 처리를 했을 때, 불필요하게 특정 컬럼의 모든 필드 정보를 로드하여 메모리 낭비가 발생하였다. 이를 개선하기 위해 QueryDSL을 사용하여 필요한 필드 정보만 DTO로 반환하도록 수정하였다.

 

 

- 리펙토링 결과 (게시글 50만건 기준)

 

 * 페이지 수가 늘어나면 늘어날 수록 성능 개선 효과가 커짐

 

 

   상세 페이지 조회 개선 ( 상세 페이지 1건 조회시 관련 쿼리문 다수 발생)

 

상세 페이지 조회시 발생하는 문제

 

  상세 페이지 조회 시, 관계 테이블 조회로 인하여 부가적인 SELECT 문이 5개가 더 추가로 발생한다. 이 현상으로 "속도 저하가 발생하지 않을까"라는 우려가 있었고 실제로 얼만큼의 속도저하가 일어나는지 확인하기 위해서 동시 요청 수 1000건을 기준으로 10번 반복 Test 를 진행하였다.

 

상세 페이지 진입시 발생하는 쿼리문

 

 

 Test 진행 후, 10000건에 대한 처리 속도가 매우 빠르게 나타났다. 그래서 실제로 10000건이 조회 되었는지 조회수를 확인하는 과정에서 동시성 문제를 발견하였다. 조회수가 10000건으로 예상하였는데 실제로는 9982건으로 조회수 누락이 발생한 것이다. 데이터를 공유하는 과정에서 업데이트 되지 못한 데이터를 다른 요청에서 받은 후, 업데이트 한 결과에 다시 덮어쓰기가 되어 카운트 수가 줄어든 것이다.

 

100명 기준 조회수 누락 현상



 이 현상을 해결하기 위해서, 최초로 낙관적락과 비관적락을 고려하였다. 충돌이 일어날 것을 예상하였기 때문에 낙관적 락( 충돌이 일어날 경우 Roll-back 이 일어남) 대신 비관적 락을 거는 것이 더 합당하다고 판단하였다. 그래서 비관적 락으로 인하여 발생할 수 있는 사이드 이펙트가 어떤것이 있을지 조사하였고 관계 테이블이 많은 경우 서로 다른 리소스를 점유하면서 데드락 현상이 발생할 수 있다는 정보를 얻었다.

 

 * Shared Lock :  트랜잭션이 데이터를 조회만 할 수 있도록 지정하는 락 (조회수를 업데이트 해야하기 때문에 사용 불가)
 * Exclusive Lock : 데이터를 변경할 때 사용하는 락,  read/write를 모두 막음 (데드락 발생 가능 하기 때문에 사용 불가)

 

첫 번째 해결 시도 (비관적 락 설정 - 충돌이 발생할 것을 예상했기 때문)

 

첫 번째 해결 방법 문제점 ( 데드락 - 관계 테이블이 많기 때문 )

 

 

 그래서 다른 해결 방법에 대해서 조사하였고, 두번째 해결 방법으로 사용하고 있는 Redis 의존성을 이용하여 (Spring-data-redis) 해결하는 방법을 검토하였다. 이 방법 역시 동시성 문제는 발생하지 않았지만 스핀락 방식에서 오는 또다른 문제점 (무한 대기 현상)이 존재하였다.  그래서 다른 해결 방법에는 어떠한 것이 있을까 조사한 결과 분산락을 이용하면 해결할 수 있다는 정보를 습득할 수 있었다.

 

두 번째 해결 시도 ( Spring Data Redis - Lettuce Spin Lock 방식 )

 

두 번째 해결 방법 문제점 ( Redis 과부하 및 무한 대기 발생 가능 )



 분산락 방식은 Redisson 이라는 의존성을 추가하여 구현할 수 있었는데, 해당 방법을 사용하기 위해서는 새로운 의존성을 도입 하여야 하고 추후, 구조를 변경해야 할 수 도있는 리스크를 가지고 있었다. 그래서 해당 방식을 적용하기 위해 시간을 소요한다면  짧은 프로젝트 기간 6주 중 기획 및 설계에 1주, 안정화 및 유저 피드백 1주를 제외한다면 또다른 중요한 기능을 구현할 수 없다는 판단이 들었다.

 

세 번째 해결 시도 ( Redisson - Distributed Lock  방식 )

 

 

 그래서 "어느 정도의 요청이 들어왔을 때 동시성 문제가 일어나는 가"를 확인하기 위해서 Test 를 진행하였고, 동시 요청 100건 부터 발생하기 시작한다는 것을 발견하였다. 현재 설계한 서비스의 규모상 같은 글을 100명이서 동시에 클릭하는 경우는 거의 없다고 판단되었고 발생하더라도 조회수 1-2 정도만 누락된다는 점에서 상대적으로 피해가 크지 않다는 것도 고려되었다.

 

 최종적으로, 조회수 관련 동시성 문제는 문제되는 상황을 인지하고 서비스의 규모가 커질 때, 개선하도록 결정하였다. 그리고 원래의 인식되었던 문제 ( 쿼리문이 5개 발생 )는 크게 속도 저하를 발생시키진 않았지만, 비효율적이라고 판단하여 Cache를 이용하도록 리펙토링을 하였다.

 

 

- 리펙토링 결과

 

 기존 구조의 문제점 

1. 관련 쿼리문이 여러개 발생

2. 상세 페이지 진입 시 마다 중복 체크 기능이 없어 무분별한 DB 업데이트 발생 (조회수 증가) 

 

개선 후 

1. Redis 를 이용한 Cashing 으로 DB 부하 감소 ( read througth, write through 전략 사용 )

2. Redis 에서 Lattuce 설정으로 무한 대기 방지 및 동시성 이슈 해결

 

 

 

 

   Main 페이지 조회 개선 ( Rank Top 10 기능으로 인해 조회마다 집계 함수 사용 )

 

  Main 페이지 조회 시, 요청이 발생할 때마다 조회수를 집계하는 문제가 있었다.( Rank Top10을 구성하기 위해 ) 게시글 수가 적을 때는 큰 문제가 없지만, 게시글이 늘어나면 속도 저하가 발생할 수 있다고 판단하였다. 

 

Main 화면 ( Rank top 10 )

 

 그래서 이를 확인하기 위해 테스트를 진행하여 속도 저하가 어느 정도 발생하는지 측정하였고, 게시글이 30만 건 이상일 때, 요청이 많아지면 DB에서 병목 현상이 발생할 수 있다는 결과를 얻었다. 이에 대한 개선책으로 Redis의 Sorted Set 자료 구조를 활용하여, 데이터를 입력할 때 자동으로 정렬되도록 저장하고, 조회 시에는 상위 10개의 데이터를 즉시 호출하는 방식으로 최적화 하였다.

 

- 리펙토링 결과

 

 

 

 4. 검색 성능 및 기능 개선 (Elasticsearch)

 

   검색 성능 개선

 

 초기 검색 기능을 구현할 때, 간단하게 Like 문을 사용하여서 개발하였다. 하지만 이방식은 속도 저하를 가져오고 다양한 검색을 지원하기 위해서는 더욱 큰 시간이 소요된다는 것으로 판단하였다. 그래서 검색을 구현하는 방법에 대해서 조사하였고 Full Text Search 방식과 Elasticsearch를 사용하는 방법을 검토하게 되었다.

 

 Full Text Search의 경우 한글 검색 관련 지원 기능을 구현하기 위해서는 코드 구현량이 많아진다는 문제점이 있었다. 또한 검색에 대한 통계를 시각화 하기위해서도 부가적인 코드가 발생한다. 그러므로 Elasticsearch의 Analyzer와 한글 Plugin 을 사용하고 Kibana를 이용하는것이 더욱 적합하다고 판단하였다.

 

기존 방식(Like 문)과 Elasticsearch 방식 비교

 

  Elasticsearch를 도입하여 검색 기능을 구현한 결과, 간단한 검색에서는 명확한 결과를 얻을 수 있었다. 하지만 짧은 단어를 검색할 때, Nori 형태소 분석기의 세밀한 토큰화로 인해 불필요한 조사가 검색 결과에 포함되어 사용자가 의도하지 않은 결과가 나오는 문제가 발생했다.

 

 이를 해결하기 위해 Filter를 사용하여 토큰화 방식을 조정하였다. 특히, 정보 습득을 위한 검색에서는 주로 명사를 대상으로 한다는 점에 주목하여, 해당 방식으로 수정하였다.

 

- 리펙토링 결과

 

 

   사용자 검색 로그 기능 추가

 

 주간 인기 검색어 기능을 구현하기 위해서 Elasticsearch 를 이용한 log 저장 기능을 구현하였다. 검색 결과가 있는 경우에만, Elasticsearch에 log 기록을 하였고 Kibana를 연동하여, 실시간으로 모니터링 할 수 있도록 하였다.

 

주간 인기 검색어 화면

 

 또한 Log를 기록할 때, 검색한 기록 자체를 저장하면 모양이 다르지만 중복되는 내용의 Keyword 가 발생하였다. 예를 들면 서울, 서울특별시, 서울시는 같은 서울 이라는 Keyword를 가지고 있지만 검색되는 방식이 달라 각각 저장되는 문제점이 있었다. 그래서 같은 내용이지만 다른 표현방식을 가지는 경우 Mapping 처리를 하여 같은 Keyword로 저장하였다.

 

경기, 경기도 중복 문제 현상

 

Keyword Mapping

 

 

- 리펙토링 결과

 

서울, 서울시, 서울 특별시 -> 서울 으로 Keyword 통일 / Kibana 적용 모니터링

 

 

   한글 관련 검색 편의 기능 추가

 

 한글 검색 관련 추가 편의 기능을 제공하기 위해 Elasticsearch 활용 방법에 대해서 조사하였다. 조사 중 javacafe 플러그인을 사용하면 초성 검색, 영타-한글 전환 제안, 한타-영문 전환 제안 기능을 간단하게 구현할 수 있다는 것을 알게 되었다. 그래서 Elasticsearch 환경에 javacafe 플러그인을 설치하려 했으나, version 불일치 문제로 설치가 진행되지 않았다.

 

 Elasticsearch 7 버전부터 다양한 기능 개선이 있었음을 공식 문서에서 확인하여 7.11.1 버전을 설치했지만, javacafe 플러그인은 Elasticsearch 6.X 버전까지만 지원해 설치가 불가능 했다. Linux 환경에서 설정 파일을 수정하여 7.11.1 버전에 맞췄으나, 코드 구조 차이로 인해 여전히 설치가 되지 않았다.

 

 에러 로그를 분석한 결과, 상속받을 클래스가 없어서 문제가 발생한 것을 알게 되었고, 공식 문서를 통해 Elasticsearch 7 버전부터는 해당 클래스를 상속받지 않고 클래스 내부에서 동일하게 사용할 수 있다는 것을 확인하였다. 그래서 플러그인의 해당 부분을 수정한 후, Java 1.8 버전으로 새로 컴파일하여 Elasticsearch 7에 적용 성공하였다.

 

 

 

 영타-한글 변환은 문제 없이 잘 작동되었지만, 한글-영타 변환 기능에서는 겹 모음이 문제를 일으켰다. 예를 들어, 'iphone'를 입력할 때 'ㅑㅔㅙㅜㄷ'으로 입력하면 정상적으로 'iphone'으로 변환되지 않는 문제가 있었다. 이는 입력 과정 중 모음이 겹쳐서 인식되지 않는 문제로  'ㅙ' 'ㅗㅐ'로 분리되면 정상적으로 변환되었다. 그래서 이를 해결하기 위해 Java 코드로 입력 시 겹 모음을 분리해주는 기능을 추가하였다.

 

 

 

 5. 게시글(여행후기) 추천 기능 개발 ( Challenge )

 

  사용자에게 더욱 흥미로운 컨텐츠를 제공하기 위해서 사용자 추천 기능을 구상하였다. 그래서 추천 알고리즘에 대해서 조사를 진행하였고 Spark 와 mahout 을 이용하는 방식이 많이 소개되어 있었다. 이 방법은 코사인 유사도를 이용하는 방식으로 유사도가 높은 단어를 수치화 하고 이를 연산하여 추천하는 방식이었다. 

 

 이 방식은 Spark는 추가적인 인프라를 구성해야 하기 때문에 현제 서비스 규모에 적합하지 않다고 판단되었다. 그래서 추천 알고리즘 방식에 대해서 검색하였고 장바구니 추천 (apriori)  알고리즘을 현재 프로젝트에 맞게 수정하여 Java 코드로 적용하였다. 

 

 구현한 알고리즘은 좋아요를 기반으로 게시글을 추천하도록 하였다. 예를 들면 부산여행(기준)을 좋아요한 유저를 조회하여 해당 유저들이 중복되게 좋아요를 누른 부산 바다여행(추천) 게시글을 추천한다.

 

좋아요 기반 추천 알고리즘

 

 하지만 이 방식은 새 글이 등록되거나 좋아요 수가 충분하지 않은 초창기 서비스에서는 적용하기 어렵다는 문제점이 있었다. 그래서 이를 해결하기 위해 게시글 내용 기반 유사도를 기준으로 추천 게시글을 선택하는 방식을 추가로 적용하였다. 해당 알고리즘은 Elasticsearch 내 BM25 알고리즘을 활용하여 추가 인프라 도입 없이 구현 후 테스트 하였다.

 

 테스트 결과, 게시글 추천이 예상보다 적게 이루어지는 문제를 발견하였다. 게시글의 내용이 대화체로 작성되어 있어, 단순 명사만으로 유사한 글을 추천하기에는 한계가 있었다. 그래서 이를 해결하기 위해 OKT 형태소 분석기를 도입하여 대화체 교정, 사용자 사전 정의 단어, 동사 및 형용사의 기본형 변환 기능을 적용하여 유사한 게시글이 추천될 수 있도록 개선하였다.

 

OKT 형태소 분석기 적용

 

OKT 분석기 및 Customize Filter 적용된 게시글 추천 기능

 

 

  앞서 말한 추천 알고리즘은 연산의 수행량이 많기 때문에, 게시글을 조회할 때마다 수행한다면 속도 저하가 발생할 것이라고 예상하였다. 그래서 스케줄러(quartz)와 batch 를 이용하여 사전에 집계를 수행하고 추천된 결과 게시글을 Cache 에 저장하고 활용하는 방식을 도입하였다.

 

게시글 추천 알고리즘으로 인한 문제와 해결방안

 

 

 또한, Batch 수행 시간이 얼마나 걸리는지 TEST를 하였고 100만 게시글 기준으로 약 47초가 소요되었다. 이를 개선하기 위해 Chunk 기반 병렬 처리 구조와 Redis의 pipeline 을 이용한 요청으로 리펙토링을 하였다. 리펙토리 하는 과정에서 최적화된 Chunk의 수와 Thread 수를 결정하기 위해서 해당 인자를 변수로 두고 Test를 진행하였다.

 

Batch 구조 개선을 통한 최적화

 

 

 테스트 결과 Thread 수가 많으면 많을 수록 빠를 것이라는 단순한 예상을 하였지만, 게시글 100개 기준으로 2개를 초과 하면 도입한 자원대비 결과가 좋지 않다는 것을 발견하였다. 또한 Chunk의 수는 1개(Chunk의 수를 1로 두는것은 잘못된 설계)를 제외하고는 Chunk의 수와 수행 속도는 무관하다는 것도 알게되었다. 

 

Thread 및 Chuck 수와 연산 속도 관계

 

batch 최적화 결과

 

 6. 사용자 Feedback

 

  짧은 기간으로 인하여 1주간 기획 및 설계를 진행하고, 2주간 기능 구현 후 유저 피드백을 진행하게 되었다. UI 가 불편한 부분이 많이 존재한다는 의견과 검색 사용시 불편함이 존재한다는 피드백을 받았다. 또한 투표 기능이 어렵다는 의견이 존재하여 다시 한번 처음 서비스를 사용한다고 가정하고 해당하는 부분을 최대한 수정하였다.

 

 불편한 UI 부분에는 다양한 기능을 버튼식으로 제공하여 사용자 편의성을 높였고, 투표 기능은 간단한 사용법을 별도로 작성해 팝업창으로 제공하였다. 본인이 맡은 검색 부분은 Nori 형태소 분석기를 커스터마이징 하여 명사와 형용사를 위주로 검색되게 하였고, 한타/영타 변환, 초성 검색 등 타 플랫폼에서 제공하는 기능을 추가로 구현하였다. 

 

 피드백 반영 결과 서비스 평점(5점 만점) 이 3.5 -> 4.2 로 약 20% 정도 상승하였다.

 

 

 

 회고

 

  짧은 기간 안에 실제로 사용할 수 있는 서비스를 만드는 것은 생각보다 어려웠지만 재미있는 경험이었다. 이전에는 Full Stack 개발로 브라우저의 데이터와 서버의 데이터를 직접 관리하였기 때문에 FE 개발자와 같이 협업에 필요한 요소들을 알지 못하였다. 하지만 이번 프로젝트로 Swagger 를 통한 문서화와 REST FUL 한 Api 설계, 내가 구현한 기능이 정확하게 어떻게 구현하는지 문서로 전달하는 것에 대해서 많은 고민을 해볼 수 있는 시간이었다. 

 

 또한, 진취적이고 책임감 있는 팀원들 덕분에 프로젝트에서 내가 맡은 부분에 완벽하게 몰입할 수 있었다. 서로가 맡은 부분을 정확하게 구현하고 마감 일정을 지켜준 덕분에 여러가지 성능 Test를 진행 해 볼 수 있었고, 서비스의 완성도를 더욱 높일 수 있었다. 또한, 힘겨운 일정 속에서도 항상 밝고 긍정적인 분위기를 만들어준 덕분에 BE 리더로서 더욱 완성도 높은 결과물을 만들 수 있도록 이끌어가는 원동력이 되었다.

 

 조금의 기간이 더 있었더라면, 더욱 다양하고 재미있는 기능을 만들 수 있었을 것 같다는 아쉬움이 남지만 좋은 동료들과 한정된 시간 안에 많은 것을 해볼 수 있었기에 성공적인 프로젝트라고 생각한다.