개발공부 & 부트캠프/[부트캠프] 회고

[데이터 엔지니어링 부트캠프]10월 4주차 회고

포리셔 2023. 10. 29. 20:45

좋았던 점

  • 지난 주말, Pypi 패키지 배포와 깃허브 블로그 배포를 마무리하는 숙제를 제출했습니다. 이와 관련된 피드백을 제공받을 수 있었습니다. 이것 외에도 1주일 간 이런저런 실습을 스스로 하고 피드백을 제공받았는데, 초보 개발자가 회사에서도 맞이할 수 있는 문제였던 만큼 두고두고 도움이 될 피드백이 많았던 것 같습니다.
  • 막판에 하루를 통으로 투자해서 실무에서 있을 수 있는 긴급상황에 대처하는 연습을 했습니다. 당연히 실전에 비하면 순하디 순한 맛이었지만 예전 PTSD도 떠오르고 안 해본 것보다는 취업한 이후에 기억을 더듬어 가며 대처할 수 있을 것 같아 좋은 경험이 될 것이라 생각합니다.

아쉬웠던 점

  • 특별히 아쉬운 점은 없었지만, 지나고서 보니 제가 1주일 간 써놨던 회고나 정리글 등을 좀 더 체계화시켜놓으면 어땠을까 싶습니다. 현재 저는 노션(Notion)이 아니라 옵시디언(Obsidian)으로 노트앱을 갈아탄 지 오래인데, 오프라인 옵시디언 환경에서 연습한 소위 제텔카스텐 기법을 블로그와 깃허브 이슈 등에도 적용해보면 어떨까 싶은 생각이 들기 시작했습니다.

    오프라인이라고 써 놓긴 했지만, 저는 옵시디언에서 작성한 문서들을 마크다운 형식으로 개인 원드라이브(Onedrive)와 깃허브 리포지터리에 저장하고 있습니다. 그러나 private 리포지터리라 남들은 열람할 수 없다

배운 점

서비스 배포(지난 주에 이어서)

블로그 배포 - fly.io

fly.io는 도커 기반으로 웹사이트를 배포하는 서비스입니다. 구글 파이어베이스(Google Firebase)와 함께 어떤 서비스를 본격적으로 배포 이전 테스트 용도로 사용할 수 있는 장점이 있으나, (초보 입장에서) 허를 찌르는 과금 정책이 도사리고 있기 때문에 AWS와 마찬가지로 서비스 테스트 배포 전에 잘 알아보시는 것을 권장합니다.

눈 뜨고 코 베인 사용료의 흔적...

  • 블로그 작성 파일이 있는 디렉터리로 들어갑니다. flyctl auth login으로 fly.io에 로그인한 뒤, flyctl launch로 fly.toml 파일을 생성합니다. 명령어를 입력하면 원하는 배포 애플리케이션 이름, 배포자가 원하는 서비스 시간대(한국에서 생성하면 자동으로 가장 가까운 일본 도쿄 시간대로 설정됩니다) 등을 설정해줍니다.
    fly.io 1
    참고로 fly.toml 파일이 생성됨과 동시에 자체적으로 도커파일을 생성해줍니다. 그런데 후술할 도커 이미지 배포 시 이렇게 생성된 도커파일을 배포할 경우 나중에 실습할 다중 URL 실습 시 웹 페이지가 제대로 출력되지 않을 수 있습니다. 자세한 내용은 아래에서 보도록 하죠.
  • 따로 fly.toml을 수정할 필요 없이 바로 flyctl depoly를 입력하면 됩니다. 단, 이때 빌드 에러가 날 경우에는 fly.toml 파일에 다음을 입력해주세요.
    [build]
      dockerfile = "Dockerfile"
    fly.io 2
    fly.io 3
    이제 터미널 맨 아래에 있는 URL로 접속하면 배포된 사이트를 확인할 수 있습니다. 이 링크는 fly.io 웹사이트에 로그인해 Dashboard 창에서 배포된 애플리케이션 이름을 클릭하면 그 안에서 다시 확인할 수 있습니다.

    도커 이미지 배포

  • 한편, 자신의 디렉터리를 도커 이미지 형태로 만들어서 도커허브에 배포하는 방법도 있습니다. 먼저 블로그 소스 코드가 있는 디렉터리에서 아래를 입력합니다. 두 번째 명령어에서 도커파일의 이름은 본인이 직접 지정할 수 있지만 저는 현재 작성 중인 디렉터리, 즉 깃허브 리포지터리의 이름과 버전 명을 붙여서 만들었습니다. 이때, 버전명을 붙이지 않고 푸쉬하면 도커허브에서는 자동으로 latest라는 태그명을 붙이게 되며, 이는 최신 버전의 도커 이미지를 뜻합니다.
$ docker login
$ docker build -t tjkpolisher/tjkpolisher.github.io:1.0.2 .
failed to fetch metadata: fork/exec /usr/local/lib/docker/cli-plugins/docker-buildx: no such file or directory

DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
            Install the buildx component to build images with BuildKit:
            https://docs.docker.com/go/buildx/

Sending build context to Docker daemon  20.52MB
Step 1/3 : FROM pierrezemb/gostatic
latest: Pulling from pierrezemb/gostatic
0cf3c901807f: Pull complete
Digest: sha256:7e5718f98f2172f7c8dffd152ef0b203873ba889c8d838b2e730484fc71f6acd
Status: Downloaded newer image for pierrezemb/gostatic:latest
 ---> 37dd39949863
Step 2/3 : COPY . /srv/http/
 ---> b2ac11f8ecc7
Step 3/3 : CMD ["-port","8080","-https-promote", "-enable-logging"]
 ---> Running in 47785afef231
Removing intermediate container 47785afef231
 ---> 1c86bd0cc9d2
Successfully built 1c86bd0cc9d2
Successfully tagged tjkpolisher/tjkpolisher.github.io:1.0.2
  • 이제 도커허브에 푸쉬하면 됩니다. 요령은 깃허브 리포지터리에 원격으로 푸쉬할 때와 유사합니다.
$ docker push tjkpolisher/tjkpolisher.github.io:1.0.2
The push refers to repository [docker.io/tjkpolisher/tjkpolisher.github.io]
81d9dd85ad88: Pushed
f347b3d1982a: Mounted from pierrezemb/gostatic
1.0.2: digest: sha256:2afc578ba8908a31ea77f4b1ce3fa073f5c058ea5d4dac8b21ef42f5c034d6bf size: 740
  • 이제 도커허브의 해당 주소로 이동하면 우리가 생성한 이미지 파일이 올라와 있는 것을 볼 수 있습니다. 그냥 두기에는 너무 횡하니까 우측 상단의 Manage Repository를 클릭해서 사용법 등을 편집하도록 합시다. 사용법은 깃허브 리포지터리의 README.md를 편집할 때와 마찬가지로 마크다운 문법으로 편집할 수 있습니다.
    도커 이미지 배포 1

    배포 과정에서의 테스트의 중요성

    공개적으로 어떤 프로그램이나 프로젝트를 배포하기 전에 로컬 환경 등에서 테스트를 할 필요성을 지속적으로 강조해왔습니다. 이때 테스트의 범위를 로컬 컴퓨터로부터 조금씩 확장시키면서 여러 번 테스트를 해 교차검증할 필요성이 있습니다. 순서를 정리하자면 Local → Dev(Firebase) → stg(fly.io) → PRD(main: GitHub) 순으로 테스트와 배포를 진행합니다.

    로드 밸런서(Load Balancer, LB)

    로드 밸런서란 서버에 가해지는 부하(=로드)를 분산(=밸런싱)해주는 장치 또는 기술을 일컫는 말입니다. 원래대로라면 NGINX나 HTTPD 등의 툴을 이용해 자동화할 수 있지만, 일단은 그 원리를 이해하는 차원에서 수작업으로 진행해보겠습니다.
    우리에게 필요한 것은 블로그 리포지터리입니다. 먼저 이 리포지터리에 docker_file/lb_nginx 디렉터리를 만듭니다. 그리고 이 디렉터리 아래에 도커파일(파일명 자체가 Dockerfile입니다)과 default.conf를 각각 아래와 같이 생성합니다.
# 도커파일
FROM nginx:1.25.1

# change conf
COPY ["default.conf", "/etc/nginx/conf.d/"]
upstream blog_servers {
        server myblog-1:80;
        server myblog-2:80;
}

server {
        listen 80;

    location / {
        proxy_pass http://blog_servers;
    }
}
  • 이제 터미널에 다음 명령어를 입력합니다. 여기서 tagname은 해당 리포지터리의 버전명으로, 상술한 것과 같이 여기서는 필수는 아닙니다. 제 경우에는 1.0.3 버전을 이용했습니다. (브랜치가 1.0.2로 되어 있는데 1.0.3 브랜치를 생성하지 않은 채 실습해서 그렇습니다...)
$ docker build -t {이미지 이름}:tagname .
$ docker run -dit --name {이미지 이름}-1 -p 8051:80 {이미지 이름}:tagname
$ docker run -dit --name {이미지 이름}-1 -p 8052:80 {이미지 이름}:tagname
$ docker build -t nginx_lb:tagname docker_file/lb_nginx

$ docker run --name nginx_lb -d -p 9052:80 --link {이미지 이름}-1 --link {이미지 이름}-2 nginx_lb:tagname

도커 이미지 배포 2
이 기능을 이용하면 여러 팀원들의 블로그에 해당하는 도커 이미지를 옮겨서 하나의 팀 단위로 옮길 수 있습니다.

팀 단위 협업 - Pypi 패키지 개발

파이널 프로젝트 조별로 모여서 협업을 해 Pypi 패키지를 개발하는 연습을 했습니다. 전체적인 과정은 백보드 - ToDo - Do - Done 순으로 업무를 하는 과정을 실습했습니다. 이번에 저희 조가 배포한 패키지는 이 링크에서 설명을 볼 수 있습니다.

인프라 구성 & 관리 기초 지식 - proxy

Scale up은 스펙 업과 같은 개념입니다. 그런데 예전 같으면 데이터 센터에서 컴퓨터를 잠깐 내리고 선을 꽂는 등 서버가 다운되는 시간이 존재했는데, 현재는 도커를 이용해 데이터 중단 시간을 단축시키거나 없앨 수 있다고 합니다.
Scale out은 서버의 부하를 줄이기 위해 서버의 개수를 늘리는 방법입니다. 서버들 각각의 IP가 있기 때문에 사용자가 특정 IP를 이용해 서버를 접속할 수 없는 문제가 있기 때문에 프록시의 힘을 빌어야 합니다.

  • 프록시(proxy): 대리자라는 뜻을 가진 이름답게, 프록시란 다른 서버에서 리소스를 찾는 클라이언트의 요청에 대한 중개자 역할을 한느 서버입니다. 즉, 클라이언트와 클라이언트가 찾고 있는 데이터를 호스팅하는 실제 서버 사이에 위치합니다. 이 때문에 클라이언트에게 프록시 서버는 실제 백엔드 서버로 나타나고(실제 웹 서버는 보이지 않음), 백엔드 서버에는 프록시 서버가 클라이언트처럼 보이게 됩니다(실제 클라이언트가 보이지 않음).

그래서 그동안 개발했던 팀원들의 블로그를 하나의 도커컴포즈 파일로 묶은 뒤 스케일 아웃할 겸, 이를 프록시화하는 실습을 해보기로 했습니다. nginx-proxy 리포지터리의 설명을 참조해서 진행했습니다. 그리고 그 전에...

FastAPI 환경 설정

FastAPI를 이용하는 방법도 알아봤습니다. 새롭게 tjkpolisher-api라는 리포지터리를 깃허브에서 생성하고 클론해옵니다. 다음으로 pdm init으로 가상환경을 생성한 뒤, 0.2.0이라는 이름의 브랜치를 만들고 pyproject.toml 파일도 수정해줍니다.
여기서 source .venv/bin/activate 명령어로 가상환경을 실행한 채로 fastapi를 설치합니다. pdm add fastapi를 이용합니다. 설치가 끝나면 pyproject.toml이 변경된 것을 알 수 있습니다. 이유인 즉, 고립된 pdm 환경에 의존성이 추가되었기 때문입니다.
FastAPI 1
실제로 이를 view로 확인해보면 이렇게 dependecies에서 fastapi가 버전명과 함께 추가되었음을 알 수 있습니다.
FastAPI 2
마찬가지로 pdm에 uvicorn[standard]을 설치해도 같은 방식으로 의존성이 추가되는 것을 알 수 있습니다.
FastAPI 3
FastAPI 4
다음으로 app이라는 디렉터리를 만들고 그 아래에 main.py라는 파일을 생성합니다. 스크립트는 공식 사이트의 예제를 그대로 사용했습니다.
FastAPI 5

from typing import Union

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}  

여기까지 실행한 결과를 브라우저에 출력하려고 하는데, fastapi 공식 홈페이지의 예제를 그대로 사용하면 에러가 발생합니다. 공식 사이트의 예제는 app 디렉터리를 만드는 것을 상정하지 않았기 때문이죠.
FastAPI 6
그러니 그냥 실행 파일을 app.main으로 바꿔서 실행해줍시다.
FastAPI 7
이제 브라우저에서 http://localhost:8000/docs로 접속할 수 있습니다.
FastAPI 8

검색 엔진 최적화(SEO)

각자의 깃허브 블로그가 네이버, 구글 등 검색 엔진에 검색되고 그 통계를 조회할 수 있게 하는 검색 엔진 최적화를 배웠습니다. 네이버와 구글에서 각각 서비스를 제공하지만, 이번에는 네이버의 서비스를 중점적으로 이용해 설정을 진행했습니다.

네이버 Search Advisor

링크로 들어갑니다. 네이버에서 서비스하는 시스템인 만큼, 네이버 계정이 반드시 필요합니다! 네이버 계정으로 로그인한 후, 웹마스터 도구 탭으로 들어가면 우리의 사이트가 검색 엔진에 어떻게 노출되는지를 알아보고 관리할 수 있습니다.
SEO 1
먼저 사이트 간단체크를 이용해봤습니다. 사이트 간단체크 탭에 들어가서 개인 깃허브 블로그의 URL을 입력하고 검색 버튼을 누릅니다. 보안을 위해 식별문자를 입력하고 나면 됩니다.
SEO 2
체크가 끝나면 어떤 특징이 있는지, 그리고 어떻게 수정할 수 있는지를 알려줍니다. 예를 들어 어떤 검색엔진에 노출되게 할 수 있는지를 정하는 robots.txt와 관련된 설정 등이 있습니다. 이 도움말을 보면서도 작업할 수 있지만, 간단체크는 하루에 10번까지 밖에 사용할 수 없을 뿐더러 사이트 관리 탭에서 제공하는 관리보다 솔루션이 자세하지 않다는 단점이 있습니다.
SEO 3
이제 사이트 관리 탭으로 들어가서 URL을 넣습니다. 문제는 사이트 소유확인, 즉 이 사이트가 실제로 너의 것인지를 확인하는 과정을 거쳐야 합니다. 우선 우리가 확인하고자 하는 URL의 주소를 넣어줘야 합니다. 여기서는 제 깃허브 블로그 주소를 넣어보도록 하죠.
SEO 4
이렇게 URL을 입력하면 이와 같이 소유확인을 위한 방법을 선택합니다. 네이버에서는 HTML 확인 파일을 이용하는 방식을 권장하는군요. 그러니 저도 HTML 확인 파일을 다운받아 확인하는 방법을 사용하겠습니다.
SEO 5
해당 파일을 다운받고 깃허브 리포지터리 디렉터리에 복사해넣습니다.
SEO 6
커밋 및 푸쉬를 하고 별다른 검증 없이 바로 풀 리퀘스트를 했습니다. 일단 빌드가 끝날 때까지 일단 기다립시다. 빌드 진행 상황은 리포지터리의 Action 탭에 들어가면 아래 사진과 같이 확인할 수 있습니다.
SEO 7
빌드가 끝나면 다시 소유확인 창으로 가 세 번째 단계의 URL에 들어가서 확인합니다.
SEO 8
여기까지 했다면 소유확인을 누른 뒤 보안문자 팝업이 뜨는데, 이것까지 입력하고 나면 소유확인이 완료됩니다.
SEO 9

검색 엔진 수집

robots.txt라는 파일은 웹페이지 루트 디렉터리에 포함되어 검색엔진에서 해당 웹페이지를 수집, 즉 검색할 수 있도록 해주는 텍스트 파일입니다. 이 파일을 깃허브 블로그 디렉터리에 추가하면 같은 방식으로 검색이 가능하도록 조치할 수 있습니다.
웹마스터 도구의 왼쪽 사이드바를 보면 검증 탭이 있습니다. 여기서 robots.txt 탭으로 들어갑니다.
robot 1
맨 아래로 가면 robots.txt 파일을 생성하는 칸이 있습니다. 여기서 팝업박스를 누르면 네이버 검색엔진(Yeti)로만 검색이 가능하게 할지(혹은 불가능하게 할지) 아니면 모든 검색엔진에 대하여 수집이 가능하게 할지(혹은 안 되게 할 지) 설정할 수 있습니다. 팝업에서 옵션만 선택하면 자동으로 이를 자동으로 작성해줍니다. 이를 다운로드받습니다.
robot 2
이제 다운로드받은 txt 파일을 깃허브 블로그의 루트 디렉터리에 넣어주시고 커밋 및 푸시합니다. 그러고 나면 다시 빌드될 떄까지 좀 기다려줘야 합니다.
그래서 아래와 같이 URL 뒤에 /robots.txt를 추가한 후 확인을 누르면 수집이 가능하다는 메시지가 뜹니다.
robot 3

오픈 그래프

오픈 그래프 태그란 사이트가 소셜 미디어, 예를 들어 카카오톡이나 페이스북, 인스타 등에 공유될 때 우선적으로 출력되는 정보들을 말합니다.
오픈 그래프 태그를 추가할 때는 html 문서에 다음과 같이 태그를 작성합니다. 이 태그는 <head> 태그 아래에 작성하게 됩니다.

<meta property="og:type" content="website">
<meta property="og:title" content="Tjkpolisher's blog">
<meta property="og:description" content="tjkpolisher의 개인 블로그입니다.">
<meta property="og:image" content="images/airplane-4974678_1280.jpg">
<meta property="og:url" content="http://tjkpolisher.github.io">

이렇게 작성하고 커밋 및 푸쉬 후 빌드가 끝나면 공유를 해봅니다. 제 카카오톡 나에게 보내기 기능으로 보냈더니 이와 같이 제가 설정한 대로 잘 뜹니다.
오픈 그래프 예시

도커 컴포즈를 이용한 블로그 통합 - 본편

NGINX

이번 작업의 기본은 NGINX를 기본으로 합니다. nginx-proxy라는 유명한 리포지터리의 설명문을 기본으로 사용했습니다. 먼저 nginx-proxy의 정보를 담은 도커 이미지를 pull 해옵니다.

$ docker pull nginxproxy/nginx-proxy:latest

다음으로 도커컴포즈 파일을 작성합니다. 도커컴포즈 파일은 nginx-proxy 리포지터리의 설명을 참고해서 아래와 같이 설정했습니다. 참고로 이 파일의 이름은 compose.yml입니다. 그리고 여기 적힌 각각의 도커파일은 제 블로그를 포함해 저희 조원들 중 일부의 블로그 도커파일을 도커 허브에서 pull 해오겠다는 뜻으로 선언한 것입니다.

version: '2'

services:
  nginx-proxy:
    image: nginxproxy/nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

  tjkpolisher:
    image: tjkpolisher/tjkpolisher.github.io
    expose:
      - "80"
    environment:
      - VIRTUAL_HOST=tjk.google.com
      - VIRTUAL_PORT=80

  8trider:
    image: seohyeongwon/8trider.github.io:1.1.0
    expose:
      - "80"
    environment:
      - VIRTUAL_HOST=8trider.example
      - VIRTUAL_PORT=80

  doxgxxn:
    image: oelrm/doxgxxn.github.io
    expose:
      - "80"
    environment:
      - VIRTUAL_HOST=doxgxxn.org
      - VIRTUAL_PORT=80

각 부분의 설명은 아래와 같습니다.

  • image: 도커허브로부터 pull 해 온 도커 이미지의 이름.
  • expose: 내부적으로 사용할 포트 번호.
  • environment
    • VIRTUAL_HOST: 본래의 깃허브 블로그 주소 URL 대신 사용할 가상의 주소.
    • VIRTUAL_PORT: 가상 주소로 들어갈 때 사용할 포트 번호.

각 인원들의 도커 이미지 이름을 확보한 뒤 다음을 터미널에 입력합니다.

$ docker compose -f compose.yml up -d

실행되었다면 윈도우 메모장을 관리자 권한으로 실행한 뒤 C:\Windows\System32\drivers\etc\hosts파일을 엽니다. 맨 아래에 아래 사진과 같이 127.0.0.1 포트에 가짜 주소들을 넣어줍시다.
도커컴포즈
저장한 후에 가상 주소로 넘어가면 이제 우리가 원하는 원래의 블로그가 제대로 출력되는 것을 볼 수 있습니다.

성능 테스트

서비스의 배포 못지 않게 중요한 것은 서비스의 유지 보수입니다. 이를 위해서는 항상 현재 서비스 중인 프로그램의 성능 테스트를 할 필요가 있습니다. 여기서는 네이버에서 개발한 nGrinder를 이용해 실시간으로 도커 컴포즈 서비스의 성능을 테스트해봤습니다.
다른 문단과 달리 이번 문단에서는 nGrinder의 사용법보다는 실전에서의 쓰임새에 대해서 할 말이 더 많습니다. 이유인 즉, 이걸로 실무에서 벌어질 법한 시나리오에 대응하는 연습을 했기 때문...!

실전 모의고사? - BMT, PoC 대응

긴급한 미팅을 앞두고 TPS 150을 안정적으로 공급할 수 있는 조건을 찾고 그 결과를 fly.io에 배포하는 상황을 가정한 연습이 진행되었습니다. 이 글 첫 문단이 fly.io였던 건 큰그림이었고
로드 테스트 전의 모습입니다. deploy는 아무것도 지정하지 않고 제 블로그만 실행하면 아래와 같이 리소스를 소모합니다.
실전 모의고사1
지금부터 여기에 각종 제약조건을 걸고 시작해보도록 하겠습니다. 일단 스케일 아웃을 시험하기 위해 다음 명령어를 통해 스케일을 조정하겠습니다. 1~5 정도로 시험하면 되겠지 싶습니다.

$ docker compose up -d --scale tjkpolisher=${원하는 스케일}

다만, 이렇게까지 했는데 무슨 짓을 해도 아웃스케일로는 안정성을 도모할 수 없을 것 같습니다. 그래서 스크립트에 sleep() 함수를 추가하는 방법을 사용하겠습니다. 테스트 설정 칸으로 돌아가봅시다. 스크립트 칸의 오른쪽의 R 버튼을 클릭합니다.
실전 모의고사2
여기로 들어오면 자바 구문의 스크립트가 뜹니다. 우리가 바꿀 것은 맨 아래에 있는 test() 함수입니다. 여기에 grinder.sleep()을 추가합니다. 괄호 안에 들어가는 숫자는 밀리세컨드 단위로, 테스트를 돌리는 와중에 일정 시간의 유예기간을 둬서 CPU에 가해지는 부하(load)를 제어하는 역할을 합니다. 저는 1초의 유예기간을 줄 것이기 때문에 1000밀리세컨드를 뜻하는 1000을 입력했습니다. 저장을 누르고 나옵니다.
실전 모의고사3
이후로는 똑같습니다. 실행을 누르고 결과를 보면 됩니다. 현재 테스트 환경은 스케일 3에 CPU는 0.04(즉, 최대 4%), RAM은 50MB로 제한한 상태입니다.
실전 모의고사4
결과를 보니 예전과 달리 처음 1분간은 선형적으로 TPS가 증가하고, 이후로는 150에서 매우 안정적으로 유지되는 것을 볼 수 있습니다. 응답 속도도 일정하고, 오류가 발생하지 않습니다.
실전 모의고사5
실전 모의고사6
실전 모의고사7
이렇게까지 하고 나서 fly.io로 배포까지 끝냈습니다. 물론 과금이 두려워서 실습 끝나자마자 일시정지시켜놨습니다

앞으로 바라는 점

  • 순수 수업은 다음 주 화요일로 마무리되고, 이제 다음 주 수요일부터 파이널 프로젝트에 돌입합니다. 금요일 오후에 멘토님도 배정이 되어 모든 준비가 되었습니다. 이제 본격적으로 풀 액셀을 밟을 때가 왔지만 언제나 그렇듯 컨디션 관리에 중점을 두고 잘 해낼 수 있으면 좋겠습니다.
  • 그리고 한 달이라는 기간이 막상 닥치고 나니 짧게도 느껴지는데, 이 기간 동안 우리가 할 수 있는 것은 최대한 챙겨가고 버릴 것은 버리면서 프로젝트 배포라는 마지막 목표까지 달성할 수 있기를 바랍니다.