• 소개말 : 본 프로젝트는 42Seoul 학생들이 선배개발자의 도움없이 순수하게 스스로 힘으로만 만든 사이트입니다. (멘토 김수보)

1. 프로젝트 소개

정부에서 제공하는 농수축산물 가격 정보 공공데이터를 가져와 작업을 했고 약 80개의 품목에 대해 평균가를 제공하고 있습니다. 개발은 2020년 4월부터 6월말까지 약 2개월에 걸쳐 진행되었습니다.

카트 세이버는 농축수산물 시세 찾기 웹 서비스로, 반응형으로 제작되었습니다.

  • 실제 사이트(클릭) : 농수축산물 시세 보기 서비스 | 카트 세이버
  • 프로젝트 이름 – 카트 세이버 (Cart Savior)
  • 프로젝트 개요 – 농축수산물의 현재 공시 가격, 등락 제공 및 온라인 몰 가격 비교 – 서비스
  • 프로젝트 일시 – 2020.04.16 ~ 2020.06.30
  • 프로젝트 참여 – 정다현(dachung), 박소현(sohpark), 임지영(jilim) (42 Seoul)
  • 프로젝트 결과 – 42 Program (42서울 내 공모전) 1등
  • 프로젝트 소개 영상 – https://youtu.be/22gcU-g5Z0Q
  • Github 링크 : pje1740/cart_savior

개발에 사용된 모든 소스와 코드는 프로젝트 레포에서 바로 확인할 수 있습니다. 해당 레포의 위키에 들어가면 서비스와 관련한 상세 내용도 포함되어 있습니다.

2. 각자의 개발 후기

3인이 함께 제작한 프로젝트로, 이 포스팅은 아래 세 후기를 합친 글입니다. 요약되거나 생략되는 부분이 있기 때문에, 분업한 내용에 대한 상세한 후기는 개별 포스팅에서 확인하실 수 있습니다.

정다현 – 데이터 크롤링, 분석
// TBA

임지영 – 프론트엔드
반응형 웹 서비스 “카트 세이버” 개발기

박소현 – 백엔드
카트 세이버(Cart Savior) – 농축수산물 가격 정보 웹 서비스- 개발 후기

3. 아이디어

카트 세이버 (Cart Savior – 정확히는 카트 세이비어지만 Cart Saver 와 비슷하게 보이는 것을 이용한 말 장난입니다 😉 돈을 아껴주는 카트 구세주! 이런 느낌으로 이해해주시면 좋을 거 같습니다) 는 개인적인 필요에서부터 출발한 프로젝트입니다. 장을 볼 때 식재료는 대체 얼마에 사야 적당히 잘 사는 걸까? 이걸 알려주는 서비스를 만들면 내가 쓰지 않을까?

3.1. 우리가 주고 싶은 핵심적인 가치는 무엇인가?

장을 보기 시작한 사회 초년생이 가장 궁금해 하는 것은, 내가 방금 집어든 이 양파가 지금 사면 비싼 걸까? 사도 되는 걸까? 에 대한 대답일 겁니다. 정확한 공시 가격과, 마트 가격의 비교를 통해 ‘내가 합리적인 소비를 하고 있구나’ ‘이번 달도 돈 없는데 진짜 똑똑하게 사야돼’에 대한 안정감을 원하지 않을까. 신선식품의 실시간 시세를 알려주고, 그래서 내가 지금 이 양파를 장바구니에 담아도 될지 안될지 알려주는 확실한 기능의 웹 사이트를 만들기로 했습니다.

우리가 프로젝트를 시작하면서 가장 중요하게 생각했던 점은 작더라도, 하나의 완전한 서비스를 만들자는 것이었습니다. 그래서 회의 첫 날부터 주차별 진도를 미리 정하고 시작했습니다. 회의록을 모두 기록하여 주차별 진도를 잘 따라가고 있는지도 확인했습니다.

저희는 그래서 처음부터 단 한가지 기능, 한 타겟에 초점을 맞췄습니다. 농축수산물의 공시 가격을 현재 장을 보고 있는 초보장봄러에게 알려주는 것. 그리고 장을 보고 있을 때 더 도움이 될 수 있는 정보는 뭐가 있을까 고민했습니다.

  • 지난 주, 지난 달, 작년 가격은 어땠는지 알려줘서 현재 가격이 어느 쯤에 있는지 파악하자
  • 인터넷 쇼핑이 더 익숙한 타겟을 위해 쇼핑몰 가격을 알려주자

기본 가격 정보와 이렇게 추가된 2가지의 기능을 선택과 집중 하여 대시보드 구성 요소를 정했습니다.

4. 기술 요약

카트세이버는 반응형 웹 서비스로 웹, 모바일 모두에서 사용하기 용이하도록 제작하였습니다. 아무래도 장을 볼 때 사용할 서비스라면 모바일 편의가 가장 중요하다고 판단하여 모바일 디자인이 중심이었습니다. 아쉽게도 앱 개발자는 팀원 중 없어서 웹으로만 제작하였습니다. 역시나 가장 많이 받은 조언도 앱으로 제작해보는 것이었는데, 향후 앱 개발을 공부하게 되거나 앱 개발자를 알게 된다면 도전해볼 영역이 될 것 같습니다.

카트 세이버를 처음 시작하면서 협업 방식에 있어 도전해본 한 가지는 철저한 분업이었습니다. 프론트와 백을 확실하게 나눠서 각자의 작업 내용을 상세하게 모르더라도 머지를 했을 때 깔끔하게 동작하는 웹 서비스를 만들고 싶었습니다.

4.1. 담당 파트

깃허브 wiki 중 프로젝트 참여자 페이지. 이름 오른쪽에 있는 영어는 42SEOUL 아이디입니다.

4.2. 사용한 기술 & 서비스

프론트: html, css, JavaScript, D3.js
백엔드: python3, pandas, flask, sqlite3, aws Elastic Beanstalk, Route 53
기타: Tableau, BeatuifulSoup, Freenom

4.3. 버전 정보

파이썬으로 작업했기 때문에 가상환경 안에서 작업했고 아래는 프로젝트 가상환경에 설치했던 라이브러리 목록으로 pip freeze 한 내용입니다.

APScheduler==3.6.3
astroid==2.4.2
beautifulsoup4==4.9.0
bs4==0.0.1
certifi==2020.4.5.1
chardet==3.0.4
click==7.1.1
Flask==1.1.2
idna==2.9
isort==4.3.21
itsdangerous==1.1.0
Jinja2==2.11.2
lazy-object-proxy==1.4.3
MarkupSafe==1.1.1
mccabe==0.6.1
numpy==1.18.3
pandas==1.0.3
pylint==2.5.3
python-dateutil==2.8.1
pytz==2019.3
regex==2020.4.4
requests==2.23.0
six==1.14.0
soupsieve==2.0
toml==0.10.1
typed-ast==1.4.1
tzlocal==2.1
urllib3==1.25.9
Werkzeug==1.0.1
wrapt==1.12.1

5. 데이터 정하기

프로젝트를 시작할 때부터, 신선식품의 오프라인 판매가는 단기간 안에 직접 수집할 방법이 없다고 판단하고 공공데이터를 사용하기로 했습니다.

서울 열린 데이터 광장, 공공데이터 포털의 농수축산물 품목별 상세조사가격, 서울 특별시 농수산식품 공사의 공공데이터 등 살펴 보았으나 어떤 데이터는 품목의 수가 너무 적었고, 품목이 많은 경우 2018년 이후 업데이트 되지 않은 경우도 있었습니다. 결국 한국농수산식품유통공사 (aT KAMIS)의 일별 부류별 도.소매가격정보 open API를 신청하여 원천 데이터로 결정했습니다.(출처)

결정한 이유로는 첫번째, 데이터 전처리에 많은 시간이 필요하지 않을 정도로 데이터를 깔끔하게 가져올 수 있었고 두번째, 식량작물, 채소류, 축산물, 수산물 등 카테고리가 적당하여 서비스 컨셉에 알맞았습니다. 또 담당자가 직접 조사해온 가격을 매일 업데이트 하는 느낌이었는데, 일요일이나 공휴일에 가격 정보가 등록되지 않았기 때문입니다. 다른 데이터에 비해 상대적으로 신뢰성이 있다고 판단되었습니다.

데이터 구조

식품 카테고리는 식량 작물(8개), 채소류(31개), 특용작물(8개), 과일류(16개), 축산물(5개), 수산물(17개)로 나뉩니다. 데이터에 가장 아쉬웠던 점은 전체 식품 품목 수가 75개로 상대적으로 적었다는 것입니다. 따라서 검색창이 사용자의 첫번째 액션으로 정해져 있는 플로우 상 ‘검색 결과가 없습니다. 다른 키워드로 검색해주세요’ 화면의 빈도가 높을 가능성이 있어 메인 화면에 클릭해 볼 수 있는 검색어를 해시태그 형태로 (#오이 #양파)붙였습니다.

6. Frontend 구성

6.1. Responsive Web

개발은 웹으로 했지만, 주로 사람들이 모바일 환경에서 서비스를 사용할 것을 고려하여 반응형 레이아웃으로 작업했습니다. 반응형이지만 Tablet은 Mobile디자인을 따르게 했기 때문에 768px을 기준으로 Mobile/PC 두 타입으로 나누어 CSS를 작업했습니다.

개인적으로 토이 프로젝트에서는 Flexbox 모듈을 많이 사용하기 때문에, 이번에도 Flexbox를 이용하여 반응형 레이아웃을 잡았습니다. breakpoints.css에서도 flex를 이용하여 컬럼별 스타일링을 진행했습니다.

breakpoints.css 일부. Bootstrap처럼 간편하게 컬럼화할 수 있도록 추가했습니다.

6.2. Animation

모션은 CSS의 animation 프로퍼티를 사용했으며 로딩 화면 및 블록 요소 등장에 사용했습니다.

사실 처음 디자인에서는 로딩 화면을 간단하게 만들었는데, 페이지 간 이동할 때 로딩 시간이 꽤 길어지자 체감 속도를 늦추기 위한 방안으로 로딩 화면을 더 재미있게 수정했습니다.

현재 서비스의 로딩 화면에 등장하는 머쓱한 일러스트는 이런 이슈로 생겨나게 되었습니다.

저희끼리는 “머쓱이”라고 부릅니다.

6.3. D3.js

version: v5

상세 페이지에 들어갈 그래프를 어떤 프레임워크로 구현할지 고민이 많았는데, 그중 D3.js를 이용하여 구현했습니다. 유력한 후보로 상대적으로 가벼운 Chart.js 가 있었으나, 평소 D3.js를 다뤄보고 싶었기에 이번에 공부도 할 겸 선택하게 되었습니다.

그래프 종류는 막대그래프를 선정했습니다. 각 항목들(당일, 지난주, 지난달, 작년)이 일정한 시간 간격을 두고 있지 않기 때문에 초기에 기획한 꺾은선 그래프로 작업할 경우 사용자에게 혼란을 줄 수 있다는 우려가 있어서 개발 도중 변경한 부분입니다.

전반적인 코드는 Bl.ocks 레퍼런스를 참고했으며, 수정이 필요한 부분 (y축 도메인 설정, 데이터가 없을 경우 등)은 D3.js 공식 문서 및 우리들의 친구 Stack Overflow를 참고하여 작업했습니다.

단순한 그래프 하나에도 엄청난 양의 전처리가 들어간다는 것을 깨달았습니다.

6.4. 계산기

계산기는 프로젝트 종료 후, 6월에 2.0 버전으로 추가한 기능 중 하나입니다. 온라인 몰에서 올라오는 물품의 단위가 다양하기 때문에 사용자가 단위를 직접 입력하여 계산할 수 있으면 좋겠다는 의견을 적극 반영한 결과입니다.

input 디자인은 샐러리를 참고하여 작업했고, 입력값만큼 width값이 알맞게 늘어나는 기능을 추가했습니다.

디자인할 땐 간단하게 생각했으나, 막상 개발해보니 쉽지 않았습니다.

실시간으로 input의 width값이 바뀌는 이벤트는 처음 작업해보는 기능이라 시행착오가 많았는데, 최종으로 input에 들어온 onkeyup 이벤트를 감지하여, 들어온 value의 길이와 비례하게 width를 변경하도록 로직을 구현했습니다.

또한 flask로부터 받는 단위 값이 100g1마리처럼 숫자와 문자가 혼합된 형태였기 때문에 정규식을 이용하여 Number타입과 String타입으로 데이터를 분리해야 했던 이슈가 있었습니다.

7. 웹 프레임워크 선정

7.1. 왜 Python과 Flask 인가?

입문의 과정이 가장 빠른 웹 프레임워크를 찾아다니던 중 파이썬 장고와 플래스크를 발견했습니다. 마침 파이썬을 복습하고 있기도 했고, pandas를 활용할 일이 있지 않을까 해서 언어는 파이썬을 고정하고 장고와 Flask 사이에서 갈등했던 것 같습니다. 결론적으로 Flask를 선택한 건 해당 프레임워크가 light-weight이고 입문의 벽이 낮았기 때문이었습니다. Treehouse 라는 구독형 개발 강의 사이트를 이용하는데, 이곳에서 Flask 입문 강의를 제공한 것도 큰 이유였습니다.

Start Learning to Code with Treehouse | Plans & Subscriptions

그리고 정말 명성대로 Flask는 입문이 쉽고 이해하기 어려운 부분이 크지 않았습니다. 강의는 각 잡고 딱 3일 동안 쉬지 않고 달려서 API 생성을 제외한 부분까지는 모두 완료했고, 그 정도만 알아도 지정한 루트(주소)에 따른 로직을 구현하여 사이트를 동작시킬 수 있었습니다. HTML과 연결하는 방식도 Jinja를 활용하기 때문에 직관적이고 검색을 통해 답을 찾기가 쉬웠습니다.

7.2. Jinja

verison: 2.11.2

python의 프레임워크인 Flask 기반으로 만들어졌기 때문에, 마크업은 Flask 내장 템플릿 엔진인 Jinja2를 사용했습니다.

// jinja
<span>메시지: {{ msg }}</span>

// Vue의 mustach 구문
<span>메시지: {{ msg }}</span>

// JSX
<span>메시지: { msg }</span>

// Pug
<span>메시지: #{ msg }</span>

템플릿 엔진은 React의 JSX, Node.js의 Pug (구. Jade)를 사용한 경험이 있기에 Jinja도 어렵지 않게 적응할 수 있었습니다. 다만 기초 문법이 위의 2가지 엔진보다는 Vue의 템플릿 문법과 더 유사하다는 느낌을 받았습니다.

<form action="{{url_for('search_functions.search')}}">
    <input class="inp_search" type="text" name="search_text" id="" placeholder="오늘의 무 값은?" required aria-required="true"/>
    <button class="btn_search" type="submit"><span class="ir">검색하기</span></button>
</form>

검색창 구현 예시. 중괄호 두 개로 둘러싸인 부분이 Jinja template이고 이를 통해 flask의 함수에게 매개변수를 전달하거나 받아온 변수를 출력하거나 할 수 있다.

@search_functions.route('/search', methods=['GET'])
def search(item_name="오류", item_price=0, date=None):
    """입력된 키워드를 기반으로 상품 리스트를 만들고 정보를 모아 리스트로 만드는 함수.
    이 함수는 검색창을 통해 키워드가 들어올 경우 실행된다."""
    search_key =request.args.get("search_text")
    search_key = key_replace(search_key)
    # search_key는 리스트 형태로 바뀐다. 검색어 전환 때문에.
    items_searched = []
    for key in search_key:
        items_searched.extend(get_all_items(key))
    # 검색 결과가 없으면if len(items_searched) == 0:
        random_keys = get_random_keywords()
        context = {'random_keys': random_keys}
        return render_template("search_no_result.html", **context)
    # item_price 테이블에서 items_searched 코드가 있는 최근일자 행을 조회
    items = get_items_from_price_table(items_searched)
    infos = get_info(items)
    session['list'] = infos
    # 금일자 dataframe에 해당 상품이 없으면if len(session['list']) == 0:
        random_keys = get_random_keywords()
        context = {'random_keys': random_keys}
        return render_template("search_no_result.html", **context)
    return render_template("search_list.html", list=infos)

검색을 눌렀을 때 실행되는 함수

8. 식자재 가격정보 가져오기

대시보드의 세번째 구성 요소인 인터넷 쇼핑몰 가격을 가져오는 코드는 https request, BeautifulSoup를 통해 간단한 코드 몇 줄로 필요한 정보만 긁어오기로 했습니다. 성격이 극명하게 다른 세 쇼핑몰을 가져오고자 했고, 마켓컬리, SSG(쓱), 하나로마트로 정했습니다. 최초에는 쿠팡도 포함하려고 했지만 아래와 같은 문제가 있었습니다.

쿠팡의 robots.txt

쿠팡은 모든 user agent에 대하여 접근을 차단하고 있습니다. 그런 이유로 쿠팡 크롤링은 더 이상 진행하지 않기로 결정했습니다.

다음은 ssg.com에서 무 가격 정보를 긁어오는 코드입니다.

import requestsfrom bs4 
import BeautifulSoup
item_name = '무'

#HTTP GET request
req = requests.get('http://www.ssg.com/search.ssg?target=all&query='+ item_name)

#HTML 소스 가져오기
html = req.text

#BeautifulSoup으로 html 소스를 python 객체로 변환한다
#첫 인자는 html소스코드, 두번째 인자는 어떤 파서인지
soup = BeautifulSoup(html, 'html.parser')

#이제 필요한 텍스트를 CSS Selector 코드를 보고 select 메서드로 가져옴
#검색 결과 3개까지
item_name = [name.text for name in soup.select('div > a > em.tx_ko')[:3]]
price = [price.text + "원" for price in soup.select('div > em')[:3]]
image = [ "http:"+ image.get('src') for image in soup.select('div > a > img.i1')[:3]]
link = ["ssg.com" + link.get('href') for link in soup.select('div.thmb > a')[:3]]

item_name, price, image, link을 차례로 가져왔습니다. 이렇게 가져오는 방식의 가장 큰 문제는 HTML 코드만 바뀌어도 와장창 한다는 것이겠죠. API를 제공하지 않는 데이터를 활용하고 싶다면 어떻게 접근해야 할지, 저작권에 위배되지는 않는지는 계속 고민해야 하는 부분 같습니다.

하나로마트 크롤링은 가격 표시가 복잡하게 되어 있어 정규표현식을 이용하여 정제했습니다.

import re
price = [re.search(r'\d*,*\d+', price.text) for price in soup.select('div > p.product-price-sale')][:3]price.group()

9. 가격비교시 단위 맞추기

인터넷 몰 가격과 비교할 때, 단위가 맞지 않는 문제가 있었는데, 간편하게 싼지 비싼지 한 눈에 알아야 하는데 사용자에게 ‘계산’이라는 복합적 사고를 하게 해선 안 된다고 생각했습니다. 원천 데이터 API의 단위는 10마리, 10kg, 500g 이런 식으로 제공되고 있습니다. 그래서 품목 수도 76개밖에 안되니까 아예 기본 단위 사전을 만들어 수동으로 입력해주었습니다 – 1개, 1마리, 100g, 1kg 같이. 그래서 상세 페이지 하단 부분이 처음엔 이렇게 되어 있었습니다.

그런데 인터넷 쇼핑몰에서도 단위는 정확하지 않아 문제가 발생했습니다. 결국 프론트의 지영님이 계산 기능을 달아 주셨습니다.

10. 배포

웹인 만큼 로컬에서만 만들어놓고 만족하기는 싫어서 반드시 배포를 하고 싶었는데, 크게 잡은 목표는 두 가지였습니다.

  1. 무료 도메인을 구매해서 사용하기
  2. 소스코드 업로드만으로 배포 완료

카트 세이버의 경우 플래스크를 붙여서 웹 프레임워크만 있었고, 웹 서버를 직접 붙이진 않았기 때문에 이 부분을 알아서 해결해주는 호스팅 서비스가 필요했습니다.

10.1. AWS Elastic Beanstalk & Route53

PaaS | 응용 프로그램 관리 | Amazon Web Services

AWS라는 이름은 개발에 입문하기 전부터도 워낙 들어왔기 때문에 언젠가는 꼭 사용해봐야겠다고 결심하고 있던 찰나, 1년 동안 무료로 사용이 가능하다고 하여 어떻게든 AWS로 배포를 해야겠다고 최종적으로 결정이 났습니다.

AWS Elastic Beanstalk, Route53과 Freenom을 이용해 Flask app을 deploy 하는 과정을 기록한 포스팅도 있으니, 세부적인 과정과 기술의 사용은 이 포스팅을 참고해주시면 감사하겠습니다.

10.2. meta 태그를 이용한 검색엔진 최적화(SEO)

메타태그는 문서의 정보를 담고 있는 HTML 태그입니다.

평소에는 아래와 같이 인코딩 방식을 정할 때나, viewport를 이용하여 반응형 웹을 만들기 위해 사용했는데, 이번 프로젝트에서 검색엔진 최적화를 위해 방법을 배워 적용했습니다.

<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

meta 태그에 title, image 등 저희 사이트에 대한 정보를 추가하여, 이를 검색엔진에게 제공함으로써 구글 등 검색 사이트에 더 잘 노출하게끔 작업했습니다. 이 기능 때문인지는 몰라도 지금 구글에 cart savior를 치면 최상단에 저희 사이트가 나와서 만족하고 있습니다.

또한 카카오톡에 링크를 공유할 때, 사진과 페이지 소개가 미리보기로 잘 정리되어 뜨는 것 또한 이 meta태그가 하는 일입니다.

11. 데이터베이스

원래는 MySQL을 공부하고 싶어 DB를 붙인다면 MySQL을 생각하고 있었습니다. 그러나 시간이 조금 제한되어 있어서 갑자기 클라이언트-서버 형태의 DB를 붙이기엔 익혀야 할 부분이 많았고, 무엇보다 로컬에서 작업하던 db를 AWS서 사용하려면 RDS 등을 통해야 해서 부가적인 툴이 많이 붙는다는 느낌을 받아 대안을 찾아보기로 했습니다. 그렇게 리서치를 하던 중 과거에 잠시 접한 적 있었던 SQLite를 발견하게 되었고 빠르게 방향을 돌렸습니다.

SQLite는 MySQL 이나 Oracle 과는 다르게 파일 형태로 db를 관리하게 되기 때문에, 이 db 파일을 소스 코드에만 포함하면 Elastic Beanstalk에서도 큰 문제 없이 동작합니다. 어떤 경우에 SQLite를 사용하는지에 대해서는 공식 사이트에서 자세하게 소개를 하고 있는데, 관련하여 블로그에 남긴 다른 글이 있으니 궁금하신 분들은 참고하시면 좋을 것 같습니다.

간단한 SQL 쿼리문은 작성할 줄 알기도 했고, ORM을 익히는 데 시간이 더 들 것 같아 코드에 바로바로 SQLite 쿼리문을 작성하여 넣는 방식으로 수정을 진행했습니다. 파이썬을 이용하여 db 파일을 연동시키고, 필요한 테이블과 데이터를 미리 세팅해두고, 라이브 서버에서 매일매일 가격 정보를 업데이트 하는 방식으로 동작시켰습니다.

db 파일로 작업을 할 때, 터미널로는 한계가 있어서 TablePlus 라는 서비스를 이용했습니다. 시각적으로 바로바로 표를 볼 수 있고, 쿼리문도 적용시킬 수 있어서 꽤 유용했습니다.

TablePlus | Modern, Native Tool for Database Management.

11.1. 스케쥴러

최종 난제는 스케쥴러의 구현이었습니다. AWS 서버에서 특정 시간대나 특정 시간 간격마다 어떠한 동작을 실행하게 하고 싶을 때 어떻게 해야하는가. 업로드하는 시점에선 6월말까지만 데이터가 쌓여있는데, 가격 정보는 매일매일 업데이트가 되기 때문에 제가 관리하지 않더라도 알아서 누적이 되어야 했습니다.

AWS를 사용한다는 전제 하에 방법은 몇 가지가 있는데 대표적으로 발견한 것은 다음 세 가지였습니다.

  1. cron.yaml 파일을 활용한다.
  2. .ebextensions 폴더에 구성 파일 정보 중 cron을 포함한다.
  3. apscheduler 라이브러리를 활용한다.

1번은 기각된 것이, 업데이트 할 때마다 cron이 중복되는 문제도 있고, deploy한 인스턴스에선 불가능하다고 하여 넘기게 되었습니다. 2번은 공식 문서를 읽어보아도 빠르게 이해가 가지 않고, 크론탭 작성 방식에 대해서 확실하게 알고 있어야 작업이 가능하여 무산되었습니다. 시간이 부족했기 때문이죠. 아마도 시간적 여유가 있었다면 2번을 확실히 알아보고 진행했을 거 같습니다. 향후 EC2로 배포하는 프로젝트는 이 방법을 선택하려고 합니다.

3번은 AWS 서버 내에서도 잘 동작하는 건 확인했지만, 한 가지 치명적인 문제는 AWS 서버의 시간을 제가 알 수 없다는 사실이었습니다. 원래는 매일 오후 2시에 db를 업데이트하려고 했는데, 아마도 한국의 시간과 다른지, 지정해둔 시간이 되어도 업데이트가 발생하지 않았습니다. AWS 서버의 시간은 어디서 확인하는지 검색을 해보아도 명확한 답은 나오지 않아 interval 방식으로 빠르게 전환하였습니다. 결국 배포한 시점으로부터 12시간마다 최근 10일치 데이터를 업데이트하고 있습니다. 당연히 중복되는 (이미 넣은) 데이터는 무시합니다.

@app.before_first_request
def initialize():
    cron = BackgroundScheduler()
    # cron.add_job(fill_db.fill_price_data, trigger="cron", hour='14', minute='00')
    cron.add_job(fill_db.fill_price_data, trigger="interval", hours=12)
    cron.start()

우여곡절 끝에 성공시키면서 느낀 점 하나라면, 너무 많은 부분을 외부 서비스에 의존하기 시작하면 섬세한 작업이 힘들어진다는 것입니다. 직접 세팅을 하고 정말 빈 os 하나만 딱 빌린다는 느낌으로 서버를 빌린다면 어렵긴 하겠지만 자유도가 높아지겠구나, 하는 깨달음이 있었습니다. 내부 사정(?)을 모르니 답답함을 많이 느꼈던 작업이었습니다.

12. 구글 애널리틱스

농수축산물 가격 정보는 저희만이 아니라 다른 사람들도 함께 보면 좋은 데이터라고 생각하여 완성한 이후 42서울 내에서 몇번 공유를 했습니다. 다만, 그 전에 작업한 것은 구글 애널리틱스를 붙이는 것이었습니다.

모인 데이터를 상세하게 읽는 방법은 모르지만, 단순하게는 DAU부터 이탈율이나 어떤 걸 검색해보는지 url을 통해 확인해볼 수 있어 흥미로웠습니다. 구글 애널리틱스는 무료 툴이고, main 페이지의 html 코드의 헤더에 구글 애널리틱스에서 제공하는 코드를 붙여넣으면 바로 연동이 되는 것으로 알고 있습니다.

13. 회고

각자 개별로 작성한 상세 회고는 위에서 드린 개인 블로그와 깃헙에서도 확인이 가능합니다. 개인적으로 세 사람의 글을 합친 제(sohpark)입장에서 간단히 느낀 바라면, 정말 협업이 아니었다면 완성하지 못했을 프로젝트라는 점입니다. 각자가 자신이 맡은 부분에서 치열하게 찾아보고 고민한 흔적이 글에 묻어나고, 개발 과정에서 모두 공유받지 못한 비하인드도 이렇게나 많았구나 싶습니다.

앞으로도 42서울에서 cadet이 늘어날 텐데, 좋은 인연을 만들고 협업하여 의미 있는 결과물을 만들어낼 수 있으면 좋겠습니다.

written by sohpark@student.42seoul.kr, jilim@student.42seoul.kr, dachung@student.42seoul.kr

edited by sohpark@student.42seoul.kr