imhamburger 님의 블로그

데이터엔지니어 부트캠프 - 도커 컨테이너 안에 Chrome 설치하기 + 컴포즈로 에어플로우 실행하기 (19주차) 본문

데이터엔지니어 부트캠프

데이터엔지니어 부트캠프 - 도커 컨테이너 안에 Chrome 설치하기 + 컴포즈로 에어플로우 실행하기 (19주차)

imhamburger 2024. 11. 23. 15:31

드디어 파이널 프로젝트를 시작했다.

 

우리팀 파이널 프로젝트 주제는 다음과 같다.

 

팀프로젝트 주제

 

현재 다양한 플랫폼에 분산된 공연 및 스포츠 경기 등의 티켓 정보를 한곳에서 확인하고 비교할 수 있도록 지원하는 플랫폼 개발

이를 통해 사용자들이 다양한 선택지를 쉽게 탐색하고 티켓 구매의 편의성을 높이고자 한다.

 

 

목표

 

사용자가 여러 티켓 플랫폼에서 제공되는 티켓 정보를 쉽게 비교할 수 있도록 하여, 공연 및 스포츠 경기 티켓 구매 과정에서 시간을 절약하고, 최적의 선택을 할 수 있는 환경을 제공한다.

 

 

나의 역할

 

데이터 수집
** 티켓 정보 크롤링 ** 티켓 홈페이지에서 티켓 정보 크롤링
** 티켓 정보 크롤링 ** 홈페이지에서 티켓 정보 크롤링
** 티켓 정보 크롤링 ** 티켓 홈페이지에서 티켓 정보 크롤링
자체 회원가입 유저정보 수집 사용자의 성별, 나이대 정보 수집
회원활동 로그 수집 사용자의 예매처 링크 클릭 로그 수집
데이터 가공
크롤링 데이터 가공 크롤링 데이터 가공
로그 데이터 가공 로그 데이터 가공
데이터 적재
크롤링 데이터 MongoDB에 적재 크롤링 데이터 MongoDB에 적재
로그데이터 S3 적재 로그데이터 S3 적재

 

한마디로 정리하자면 전반적인 데이터 ETL 담당이다.

 

 

기술 스택

 

1. Apache Kafka

2. Apache Airflow

3. Docker compose

4. MongoDB

 

 

이번주는 사실 대부분의 시간을 팀원분들과 ERD 설계 / 화면설계 / 아키텍쳐 설계를 진행하였다.

이러한 기초공사가 잘 되줘야 나중에 고생하지 않기 때문에 확실하게 정하고 넘어가고 싶었다.

(그렇지만 중간중간에 바뀌는 건 어쩔 수 없다...)

 

 

1. 크롤링 기능 구현

 

내가 해야할 일은 먼저 크롤링을 해오는 것!!

내가 크롤링할 웹사이트는 동적 웹페이지이기 때문에 Selenium을 사용해야 했다.

셀레니움보다 BeautifulSoup을 쓰고 싶었다. 시간을 아끼기 위해! 그러나 동적 웹페이지에서는 가져올 수 없어서 셀레니움을 사용하였다.

 

크롤링은 class 태그만 잘 확인하고 넣어주니 그리 어렵지는 않았지만, 예외처리가 너~~~무 많았다.

(예외처리에서 삽질을...)

 

어려웠던 점

 

우선 옛날 것까지 긁어오려니 그 당시에는 정형화되지 않은 페이지들이 많았고 중요한 건 오류페이지가 404에러가 아니라는 것...

그리고 페이지가 없는 것도 오류페이지랑 같은 메세지를 담고 있었다. 그니까... 페이지가 있지만 오류인 것과 페이지가 없는게 같은 404에러도 아닌 같은 메세지를 담고 있어 이거를 어떻게 해야하나 고민이 많았다.

 

왜냐하면 마지막페이지가 되면 크롤링을 멈추게끔 해야하는데 페이지가 없어도 오류가 아니니... 애매했다.

지금까지 올라온 티켓 정보들을 살펴보니 번호가 5만이 넘어있었고, 옛날 것들에서 페이지가 오류인 것이 많았기에 번호가 5만 미만이면 그냥 continue 했다. 그리고 5만이 넘는데 오류페이지 인 것은 마지막 페이지로 인식하고 크롤링을 종료한다.

(하드코딩이긴 하지만... 어쩔 수 없었다.. 페이지가 너무 뒤죽박죽이라 너무 고생했다..ㅠ) 

 

 

2. 에어플로우 개발 환경 세팅

 

나중을 위해서라도 개발한 것을 배포할 때 편하게? 하기 위해 도커 컴포즈로 환경을 세팅하고 싶었다.

그럼 이것저것 설치해줄 필요없이 아무 컴퓨터에서 docker compose up !만 해도 잘 구동이 되니까! (팀원분들이 테스트하기에도 편하고)

근데 그만큼 나는 고생한다는 사실.....

 

에어플로우를 로컬에서만 써봤지 도커로 하는 건 처음이었어서 하나를 해결하면 또 다른 에러가 나타나는 마법!

그래도 오늘까지 개발 환경 세팅은 마친 것? 같다.

 

docker-compose.yaml

x-airflow-common-env: &airflow-common-env
  AIRFLOW__CORE__EXECUTOR: CeleryExecutor
  AIRFLOW__CORE__FERNET_KEY: ''
  AIRFLOW__CORE__SQL_ALCHEMY_CONN: 'postgresql+psycopg2://airflow:airflow@postgres/airflow'
  AIRFLOW__CELERY__BROKER_URL: 'redis://redis:6379/0'
  AIRFLOW__CELERY__RESULT_BACKEND: 'db+postgresql://airflow:airflow@postgres/airflow'
  AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: "false"
  AIRFLOW__CORE__LOAD_EXAMPLES: "false"
  AIRFLOW__SCHEDULER__ENABLE_HEALTH_CHECK: 'true'
  AIRFLOW__WEBSERVER__DEFAULT_USER: "true"

x-airflow-common-volumes: &airflow-common-volumes
  - ${AIRFLOW_PROJ_DIR:-.}/dags:/opt/airflow/dags
  - ${AIRFLOW_PROJ_DIR:-.}/logs:/opt/airflow/logs
  - ${AIRFLOW_PROJ_DIR:-.}/config:/opt/airflow/config
  - ${AIRFLOW_PROJ_DIR:-.}/plugins:/opt/airflow/plugins

services:
  # Airflow Webserver
  airflow-webserver:
    image: apache/airflow:2.10.3
    container_name: airflow-webserver
    ports:
      - "8080:8080"
    depends_on:
      - airflow-init
      - airflow-scheduler
      - airflow-worker
      - kafka
    environment:
      <<: *airflow-common-env
    volumes:
      - ${AIRFLOW_PROJ_DIR:-.}/dags:/opt/airflow/dags
      - ${AIRFLOW_PROJ_DIR:-.}/logs:/opt/airflow/logs
      - ${AIRFLOW_PROJ_DIR:-.}/config:/opt/airflow/config
      - ${AIRFLOW_PROJ_DIR:-.}/plugins:/opt/airflow/plugins
    restart: always
    command: webserver

  # Airflow Scheduler
  airflow-scheduler:
    image: apache/airflow:2.10.3
    container_name: airflow-scheduler
    depends_on:
      - airflow-init
      - kafka
    environment:
      <<: *airflow-common-env
    volumes:
      - ${AIRFLOW_PROJ_DIR:-.}/dags:/opt/airflow/dags
      - ${AIRFLOW_PROJ_DIR:-.}/logs:/opt/airflow/logs
      - ${AIRFLOW_PROJ_DIR:-.}/config:/opt/airflow/config
      - ${AIRFLOW_PROJ_DIR:-.}/plugins:/opt/airflow/plugins
    restart: always
    command: scheduler

  # Airflow Worker
  airflow-worker:
    build: .
    container_name: airflow-worker
    depends_on:
      - airflow-init
      - kafka
    environment:
      <<: *airflow-common-env
      GOOGLE_CHROME_BIN: "/usr/bin/google-chrome"
      CHROMEDRIVER_PATH: "/usr/local/bin/chromedriver"
      AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
      AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
      AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION

    volumes:
      - ${AIRFLOW_PROJ_DIR:-.}/dags:/opt/airflow/dags
      - ${AIRFLOW_PROJ_DIR:-.}/logs:/opt/airflow/logs
      - ${AIRFLOW_PROJ_DIR:-.}/config:/opt/airflow/config
      - ${AIRFLOW_PROJ_DIR:-.}/plugins:/opt/airflow/plugins
    restart: always
    command: celery worker
    deploy:
      resources:
        limits:
          memory: 2g
          cpus: "1.0"

  # Airflow Triggerer
  airflow-triggerer:
    image: apache/airflow:2.10.3
    container_name: airflow-triggerer
    depends_on:
      - airflow-init
      - kafka
    environment:
      <<: *airflow-common-env
    restart: always
    command: triggerer

  # Airflow Database Initialization
  airflow-init:
    image: apache/airflow:2.10.3
    container_name: airflow-init
    environment:
      <<: *airflow-common-env
        #AIRFLOW__WEBSERVER__DEFAULT_USER: "true"
    entrypoint: >
      bash -c "
      airflow db upgrade &&
      airflow users create \
      --username $AIRFLOW_USERNAME \
      --firstname $AIRFLOW_FIRSTNAME \
      --lastname $AIRFLOW_LASTNAME \
      --role Admin \
      --email $AIRFLOW_EMAIL \
      --password $AIRFLOW_PASSWORD &&
      echo 'Airflow Initialization Complete'"
    restart: "no"

  # Kafka Broker
  kafka:
    image: confluentinc/cp-kafka:7.5.0
    container_name: kafka
    ports:
      - "9092:9092"
    depends_on:
      - zookeeper
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    restart: always

  # Zookeeper for Kafka
  zookeeper:
    image: confluentinc/cp-zookeeper:7.5.0
    container_name: zookeeper
    ports:
      - "2181:2181"
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    restart: always

  # Airflow Metadata Database (PostgreSQL)
  postgres:
    image: postgres:15
    container_name: postgres
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: airflow
      POSTGRES_PASSWORD: airflow
      POSTGRES_DB: airflow
    restart: always
    volumes:
      - postgres-data:/var/lib/postgresql/data

  # Redis (Celery Backend)
  redis:
    image: redis:7
    container_name: redis
    ports:
      - "6379:6379"
    restart: always

volumes:
  postgres-data:

 

카프카와 주키퍼는 크롤링을 배치작업을 돌리고 S3에 적재할 때 데이터가 안정적으로 저장이 될 수 있게끔 하려고 우선 기본 세팅만 해놓은 상태이다.

 

이 docker compose yaml 파일을 만들면서 온갖 에러를 만났다...

 

 

나의 에러 리포트

 


에러 1)

WARNING: The directory '/home/airflow/.cache/pip' or its parent directory is not owned or is not writable by the current user.kafka.errors.NoBrokersAvailable: NoBrokersAvailable

 

[2024-11-21 01:43:00,555] {process_utils.py:137} INFO - Output:
[2024-11-21 01:43:02,254] {process_utils.py:141} INFO - WARNING: The directory '/home/airflow/.cache/pip' or its parent directory is not owned or is not writable by the current user. The cache has been disabled. Check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
[2024-11-21 01:43:02,256] {process_utils.py:141} INFO - ERROR: Can not perform a '--user' install. User site-packages are not visible in this virtualenv.
[2024-11-21 01:43:02,854] {process_utils.py:141} INFO - WARNING: You are using pip version 21.0.1; however, version 21.3.1 is available.
[2024-11-21 01:43:02,855] {process_utils.py:141} INFO - You should consider upgrading via the '/tmp/venvyok668il/bin/python -m pip install --upgrade pip' command.
[2024-11-21 01:43:02,947] {taskinstance.py:1482} ERROR - Task failed with exception

 

발생이유

결과적으로 공식 버전과 다른 예전 메뉴얼로 진행하여 발생한 문제

 

해결방법

예전 버전이 아닌 최신 버전으로 수정하니 해결 (참고문서)

 

 

 

에러 2) Cannot connect to amqp://guest:**@127.0.0.1:5672//: [Errno 111]

[2024-11-21 23:50:04,206: ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//: [Errno 111] Connection refused.
Trying again in 12.00 seconds... (6/100)

 

발생이유

RabbitMQ 에러가 왜 나는지? 정말 이 에러 원인을 찾는데 제일 어려웠다. 왜냐하면 나는 redis로 브로커를 사용중인데 계속 래빗mq 브로커 연결이 안된다고 하니...

이 오류때문에 웹서버는 동작하나 에어플로우 워커가 task 를 다음 단계로 진행하지 못하는 문제가 있었다.

 

근데 yaml 코드에서 추가 된 부분이 있었는데...

워커에 user를 root로 설정하고 git 을 설치하게 하였다. git을 설치하게 한 이유는 에어플로우 dags에서 PythonVirtualOperator 를 써서 git 허브 주소에서 크롤링 기능을 설치하기 때문이다.

근데 컨테이너에 git이 설치가 되지 않으면 실행이 안되어 추가한 부분이었다.

 

따라서, 공식가이드의 yaml 의 worker 부분의 cmd 부분과 이를 위해 컨테이너 수행 권한을 root 로 변경하면서 발생한 문제였다.

 

해결방법

에어플로우 worker 이미지를 Dockerfile 을 별도로 만들어 빌드하니 해결되었다.

FROM apache/airflow:2.10.3

USER root
RUN apt update && apt install -y git

USER airflow

 

먼저, root 유저로 들어가 git을 설치한 후 다시 유저를 디폴트인 airflow로 바꾸는 코드이다.

 

 

 

에러 3) 에어플로우 python가상오퍼레이터 사용 시 chrome이 실행되지 않는 문제

Output:
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO - Traceback (most recent call last):
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -   File "/tmp/venv-call3h8x688t/script.py", line 49, in <module>
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -     res = get_data(*arg_dict["args"], **arg_dict["kwargs"])
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -   File "/tmp/venv-call3h8x688t/script.py", line 21, in get_data
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -     get_data = scrap_data()
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -                ^^^^^^^^^^^^
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -   File "/tmp/venvx7iau3nj/lib/python3.12/site-packages/crawling/crawler.py", line 15, in scrap_data
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -     driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -   File "/tmp/venvx7iau3nj/lib/python3.12/site-packages/selenium/webdriver/chrome/webdriver.py", line 45, in __init__
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -     super().__init__(
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -   File "/tmp/venvx7iau3nj/lib/python3.12/site-packages/selenium/webdriver/chromium/webdriver.py", line 55, in __init__
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -     self.service.start()
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -   File "/tmp/venvx7iau3nj/lib/python3.12/site-packages/selenium/webdriver/common/service.py", line 108, in start
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -     self.assert_process_still_running()
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -   File "/tmp/venvx7iau3nj/lib/python3.12/site-packages/selenium/webdriver/common/service.py", line 121, in assert_process_still_running
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO -     raise WebDriverException(f"Service {self._path} unexpectedly exited. Status code was: {return_code}")
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO - selenium.common.exceptions.WebDriverException: Message: Service /home/***/.wdm/drivers/chromedriver/linux64/114.0.5735.90/chromedriver unexpectedly exited. Status code was: 127
[2024-11-22, 03:43:16 UTC] {process_utils.py:194} INFO - 
[2024-11-22, 03:43:17 UTC] {taskinstance.py:3311} ERROR - Task failed with exception
Traceback (most recent call last):
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/models/taskinstance.py", line 767, in _execute_task
    result = _execute_callable(context=context, **execute_callable_kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/models/taskinstance.py", line 733, in _execute_callable
    return ExecutionCallableRunner(
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/utils/operator_helpers.py", line 252, in run
    return self.func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/models/baseoperator.py", line 417, in wrapper
    return func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/operators/python.py", line 505, in execute
    return super().execute(context=serializable_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/models/baseoperator.py", line 417, in wrapper
    return func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/operators/python.py", line 238, in execute
    return_value = self.execute_callable()
                   ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/operators/python.py", line 870, in execute_callable
    result = self._execute_python_callable_in_subprocess(python_path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/airflow/.local/lib/python3.12/site-packages/airflow/operators/python.py", line 588, in _execute_python_callable_in_subprocess
    raise AirflowException(error_msg) from None
airflow.exceptions.AirflowException: Process returned non-zero exit status 1.
Message: Service /home/***/.wdm/drivers/chromedriver/linux64/114.0.5735.90/chromedriver unexpectedly exited. Status code was: 127

 

발생이유

이 문제는 Selenium에서 사용하는 chromedriver가 실행되지 않고 종료되었기 때문에 발생한다.

Status code was: 127은 일반적으로 실행 파일이 올바르게 설치되지 않았거나, 실행에 필요한 라이브러리가 누락되었음을 나타낸다.

 

해결방법

에어플로우 worker Dockerfile에 git 뿐만 아니라 Chrome도 별도로 설치해주어야 했다.

나는 크롤링 기능에 이미 크롬이 있으니까 가능할 줄 알았는데 그게 아니었고 별도로 설치해주어야 한다고 한다.

처음에는 Dockerfile에 명령어들을 다 넣었었는데, 너무 길어서 install.sh 로 따로 명령어들을 빼주고 Dockerfile이 그걸 복사해 실행하는 방향으로 설정해주었다.

 

install.sh

#!/bin/bash
set -e  # 에러 발생 시 즉시 종료

# 패키지 업데이트 및 필수 라이브러리 설치
apt update && apt install -y \
    wget curl unzip libglib2.0-0 libnss3 libgconf-2-4 libfontconfig1 \
    libxrender1 libxi6 libxtst6 libx11-xcb1 x11-utils git

# Google Chrome 설치
wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt install -y ./google-chrome-stable_current_amd64.deb
rm google-chrome-stable_current_amd64.deb

# Chromedriver 설치
wget -O /tmp/chromedriver.zip "http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip"
unzip /tmp/chromedriver.zip -d /usr/local/bin/
chmod +x /usr/local/bin/chromedriver
rm /tmp/chromedriver.zip

# AWS CLI 설치
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
./aws/install
rm -rf awscliv2.zip aws

# 불필요한 파일 정리
apt-get clean
rm -rf /var/lib/apt/lists/*

 

여기서 주의해야할 점은 나의 경우 Chromedriver에서 계속 문제가 생겨 찾아보니 없는 버전을 설치하고 있었다...

 

+ 처음에 Dockerfile에만 명령어들을 넣었을 때는 잘 compose up이 되는데 막상 컨테이너 안에 들어가서 chrome 버전을 확인하니 설치가 전혀 안되어 있었다.

docker exec -it airflow-worker bash
google-chrome --version
chromedriver --version

 

여기저기 블로그에서 본 것들을 넣었던 것인데..옛날 글이라 그런지 버전이 사라졌나보다...(에러를 잡는데 꽤나 시간이 걸렸답니다!)

그래서 내가 참고한 사이트는 여기!

 

Dockerfile

FROM apache/airflow:2.10.3

USER root

COPY install.sh /
RUN bash /install.sh

USER airflow

 

Dockerfile이 훨씬 간단해졌다!

 

 

 

일주일을 보내면서...

 

이번 한 주는 정말 정신없고 힘들었던 시간이었다. 체력적으로도 정신적으로도 굉장히 지치는 일이 많았다.

프로젝트를 진행하면서 매일같이 남아서 오류를 해결하고, 팀원들과 회의를 거듭하며 문제를 해결하려고 노력했지만, 그 과정이 쉽지 않았다. 밤늦게까지 작업하다 보니 잠도 제대로 못 자고 하루하루를 버티는 느낌이었다. 아마 당분간은 이런 날들이 계속되지 않을까 싶다.

특히 처음 프로젝트를 시작할 때, 익숙하지 않은 작업들이 너무 많아서 힘들었다. ERD 설계부터 요구사항 정의서 작성, 기능 정의서 정리 등 하나하나가 낯설었고, 어디서부터 시작해야 할지 막막하기도 했다. 하지만 팀원들과 함께 피드백을 주고받으며 점차 개선되었고, 이제는 업무를 체계적으로 진행할 수 있게 되어 한결 마음이 가벼워졌다. 처음에는 불확실하고 혼란스러웠지만, 점점 나아지는 과정을 보면서 스스로도 성장하고 있다는 것을 느낄 수 있었다.

 

그리고 오늘 ADsP 사전? 결과가 나왔다!

 

 

합격이다!

 

합격이어서 기분은 좋았다. ㅎ 사실 시험본 날은 멘탈이 탈탈털려서... 걱정이 이만저만이 아니었는데 다행히도 한번에 합격할 수 있어서...나 자신을 칭찬해..

 

 

 

앞으로 나의 방향

 

다가오는 12월 말까지는 파이널 팀프로젝트에 모든 에너지를 쏟아부을 것 같다.

정신없이 바쁘겠지만, 이번 프로젝트를 통해 배울 수 있는 것들이 많아서 기대도 되고, 또 그만큼 부담도 느껴진다.

프로젝트의 결과물이 완성되는 시점이 되면, 그동안 작업했던 내용과 결과를 정리해서 포트폴리오를 작성해볼 계획이다.

이번 프로젝트를 진행하면서 마치 그동안 몰랐던 것들을 한꺼번에 다 배우는 기분이다.

새로운 기술과 개념들을 익히는 과정이 쉽지는 않지만, 그만큼 성장하고 있다는 걸 느낄 수 있어서 보람도 크다. 힘들긴 하지만 좋은 결과를 내기 위해 최선을 다해야겠다.

언젠가 이 순간을 뒤돌아보며 "참 열심히 살았다"고 웃을 수 있기를 바라면서, 다시 한번 스스로를 다잡아 본다. 화이팅!