SKN_13th

[플레이데이터 SK네트웍스 Family AI 캠프 13기] 3주차 회고

8000gam 2025. 4. 13. 17:14

지난 주 회고를 남기고 또 한 주가 지났다. 이제 캠프 생활은 완벽히 적응한 것 같지만, 출퇴근길의 1호선은 평생 적응하기 어려울 것 같은 느낌이다. 그래서 3주가 지난 요새도 편하게 오가는 방법을 연구하고 있다. 집에서 버스를 타고 구로역에 가는 것이 거리 상으로는 가장 합리적이지만, 1호선은 이미 신도림역에서 많은 승객을 태워 오기 때문에 매번 첫 열차를 보내고 다음 열차를 타게 된다. 그래서 조금은 돌아가더라도 영등포역으로 간 뒤에 비교적 여유로운 1호선을 타고 출근한다. 퇴근할 때는 아예 늦게 나서는 게 좋겠다. 오후 7~8시 사이의 구로 혹은 영등포 종착 열차를 타고 가면 차량 내부가 매우 조용하고 쾌적하다. 복습도 할 겸 일석이조겠다는 생각이다.

 

회고를 남기려 캠프에서 보낸 지난 일들을 돌이켜보니 한 주 사이에 많은 일들이 있었다. 다 끄적이기에는 너무나도 장황해질 것 같아, 이번 회고의 목표는 채움이 아닌 비움이 될 것이다.

💻 What?

1. SQL

지난 주에 못 다한 SQL 강의를 마쳤다. SELECT문 기본 문법에 대해 간략하게 돌아본 뒤 JOIN에 대해 배웠다. 간단하게 설명하면 서로 관련성이 있는 두 테이블의 데이터를 동시에 조회하는 것을 도와주는 구문이다. 크게 INNER JOINOUTER JOIN으로 나뉜다. 그리고 서브쿼리에 대해서도 배웠다. 서브 쿼리는 쉽게 생각 하면 _QUERY IN QUERY_이다. 아래 쿼리는 FROM절에 붙는 서브쿼리인 인라인 뷰(Inline View) 예시로, '서울'에 위치한 부서의 부서_ID 목록을 먼저 추출한 뒤, 해당 부서에 속한 직원들의 이름과 부서명을 조회해준다.

-- subquery
SELECT 
    emp.emp_name,
    dep.dept_name
FROM 
    employees as emp
JOIN        -- JOIN은 "So What?"에서 후술한다.
    (SELECT dept_id, dept_name FROM departments WHERE location = '서울') as dep
ON
    emp.dept_id = dep.dept_id;

2. 웹크롤링

웹 스크래핑과 크롤링에 대해 배웠다(둘은 엄연히 다르지만 이후 모두 크롤링으로 통일한다). 웹 사이트의 구조에 대해 알고 있어야 웹 사이트의 정보를 읽어 낼 수 있기 때문에, 관련 언어인 HTML(HyperText Markup Language)CSS(Cascade Style Sheet)을 간단하게 배운 뒤, 웹 크롤링 관련 라이브러리들을 다뤄보는 시간을 가졌다.

import requests
from bs4 import BeautifulSoup

url = "https://8000gam.tistory.com"
res = requests.get(url)
soup = BeautifulSoup(res.text, "lxml")

3. 1st mini project

3주간 배워온 것들을 토대로 첫번째 미니 프로젝트를 실시했다. 기간은 이틀이며, 대주제는 크롤링, DB 구축, DB를 활용하여 사용자의 입력을 처리하는 App 개발이었다.

😮 So What?

1. 치열하게 공부하자, JOIN.

강사님께 저번 주 SQL 수업에서 예고하셨듯, 복습하는 지금도 SELECT 기본 문법보다 한 차원 어려워졌음을 느낀다. JOIN은 예시를 통해 한 차례 제대로 짚고 넘어가는 것이 좋을 듯하다.

 

기업의 HRM 팀에서 부서 테이블직원 테이블을 관리하고 있다고 가정하자. 부서 테이블부서명과 해당 부서가 위치한 지사의 지역, 개별 부서명의 식별자 역할을 하는 고유의 부서_ID, 총 3종의 정보를 다루고, 직원 테이블직원명, 소속 부서, 개별 직원의 식별자 역할을 하는 고유의 직원_ID, 총 3종의 정보를 다룬다. 여기서 직원 테이블소속 부서는, 부서 테이블부서_ID를 활용(참조)한다. 직원 별 근무지 데이터가 필요한 상황이 생겼을 때, 직원 테이블만으로는 이를 해결하기 어렵다. 이 때, 직원 테이블소속 부서라는 정보를 부서 테이블에서 가져와 사용하고 있다는 점을 이용해, 부서 테이블의 다른 정보들도 사용할 수 있다.

 

여기서 기능에 따라 INNER JOINOUTER JOIN로 나뉘는데, OUTER JOIN은 다시 LEFT, RIGHT, FULL 세 가지 방식으로 구분된다. 이 중 아래에서 예시로 들 LEFT OUTER JOIN은 기준이 되는 왼쪽 테이블의 모든 데이터를 유지하며, 일치하는 데이터가 없는 경우에는 오른쪽 테이블의 값을 NULL로 채운다.

 

-- INNER JOIN
SELECT     
    emp.emp_id AS "ID", 
    emp.emp_name AS "이름", 
    dep.dept_name AS "부서명", 
    dep.location AS "근무지"
FROM
    employees AS emp            
JOIN    -- 'INNER'는 생략 가능
    departments AS dep
ON        -- 공통된 열(column)
    emp.dept_id = dep.dept_id;    

-- OUTER JOIN(LEFT)
SELECT     
    emp.emp_id AS "ID", 
    emp.emp_name AS "이름", 
    dep.dept_name AS "부서명", 
    dep.location AS "근무지"
FROM
    employees AS emp            
LEFT JOIN    
    departments AS dep            
ON    
    emp.dept_id = dep.dept_id;    -- 공통된 열(column)

 

두 쿼리 모두 직원 테이블부서 테이블에 대해, 부서_ID(dept_id)가 같은 경우의 데이터를 조회하지만, 약간의 차이가 있다. 가령 부서를 아직 배정받지 않은dept_id = null 직원이 있다고 가정하자.

 

INNER JOIN을 사용할 경우, 해당 직원은 조회 결과에서 아예 제외된다. 왜냐하면 NULL 값은 어떤 값과도 같지 않기 때문에, ON emp.dept_id = dep.dept_id 조건을 만족하지 못하기 때문이다.

 

반면 LEFT JOIN을 사용하면, 해당 직원의 정보는 여전히 결과에 포함되며, 해당 직원의 부서명과 근무지 정보는 NULL 표시된다. 즉, LEFT JOIN기준이 되는 왼쪽 테이블(여기서는 employees)의 모든 행을 포함시킨 정보를 조회한다. 반대로 RIGHT JOIN이었다면 부서 테이블이 기준이 되어, 직원이 속하지 않은 부서도 조회 결과에 포함되며, 해당 부서에 배정된 직원이 없는 경우 이름(emp_name)과 같은 직원 관련 정보는 NULL로 표시되었을 것이다.

2. 프로젝트: 첫 개발은 계획대로 되지 않아🎵

결론부터 말하자면, 무사히 잘 마쳤다. 팀은 총 5명으로 구성되었고, "자동차보험 특별 약관 정보 비교 시스템"이라는 주제를 선정했다. 우리 팀은 대주제에 맞게 과업을 크게 크롤링, DB, 개발 세 가지로 나누어 진행했다. 나는 그 중 크롤링과 개발에 기여했다.

크롤링에 대해 얘기하자면, 대학생 시절 교양 수업에서 "리그오브레전드 승률 예측 모델 구현"이라는 주제로 팀 프로젝트를 수행했을 때 라이엇 오픈 API를 이용하여 게임 데이터를 크롤링했던 기억이 있었다. 오픈 API를 이용하는 경우, 대부분은 필요한 데이터를 딕셔너리 형태로 담은 url을 돌려줄 뿐이기 때문에, HTML 관련 지식이 없어도 유튜브 선생님들을 따라하기만 하면 쉽게 긁어올 수 있다. 이런 감각으로, 이번 팀 프로젝트도 크롤링은 문제되는 일은 없을 것이라고 생각했다. 하지만 이는 단지 착각일 뿐이었다. 실제 웹 사이트에서 데이터를 긁어오는 것은 난이도가 달랐다. 이틀 동안 배운 HTML 지식으로 실제 웹 사이트를 크롤링하는 것은 생각보다 시간이 소요되는 작업이었다.

 

개발에도 기여하게 되었는데, 사실 나는 개발을 단 한 번도 해본 적이 없다. 내가 해온 코딩은 오직 문제 제기, 가설 검정, 모델링, 결과 평가 등 학문적인 궁금증 해소, 연구 과제를 위한 코딩이고(그마저도 한두번 뿐이다), 서비스를 만들어 낸 것은 이번이 처음이다. 주어진 시간이 이틀 뿐임을 고려했을 때, 처음 배운 HTML과 CSS로 프론트를 개발하는 것보다, python 언어로 여러 기능을 구현할 수 있는 streamlit이라는 라이브러리를 사용하는 게 좋겠다는 생각으로 해당 라이브러리를 열심히 공부했다. 개발하면서 중점적으로 둔 부분은 1주차에 배웠던 객체 지향 프로그래밍(OOP; Object-oriented Programming)을 실현하는 것이었다. 페이지 이동, 뒤로가기, 페이지 렌더링 등 App에 필요한 모든 기능을 함수화했다. 이렇게 하니, 나중에 비슷한 기능이 필요할 때 기존 함수를 참조해서 약간만 변경&수정한 새로운 함수를 만들어주고, 오류가 발생하면 어떤 함수에서 어떤 부분이 문제였는지 바로바로 알 수 있어서 좋았다.

import streamlit as st

# st.session_state는 쉽게 말하면 사용자 세션별로 데이터를 저장할 수 있는 임시 저장소이다.  
# 버튼 클릭이나 페이지 이동 등 앱 내부 상태를 기억하고 제어할 때 사용된다.

# 초기 상태 설정. current_page랑 history는 내가 만든 커스텀 어트리뷰트.
if "current_page" not in st.session_state:
    st.session_state.current_page = "home"
if "history" not in st.session_state:
    st.session_state.history = []
# 이거 처음 써보는데 진짜 유용하다. 페이지 로그를 스택(LIFO)으로 관리해서 페이지 이동 구현.

# 함수: 페이지 이동

# 해당 페이지로 이동(forward)
def go_to(page: str) -> None:
    st.session_state.history.append(st.session_state.current_page)
    st.session_state.current_page = page

# 이전 페이지로 이동(backward)
def go_back()   -> None:
    if st.session_state.current_page != "home":
        st.session_state.current_page = st.session_state.history.pop()  

# 각종 함수들
# def ~~

 

돌이켜 보니 정말 재미있고 알찬 이틀이었다. 아주 멋진 팀원들과 만나게 되어 서로 소통도 유기적이었고, 결과물을 만드는 과정에서 지금까지 배워온 것들이 여기저기 사용되는 것들을 실시간으로 경험할 수 있어 좋았다. 협업하는 과정에서 GitHub도 브랜치를 분기해서 사용했는데, 나중에 전부 병합하여 각자의 코드가 합쳐진 전체 코드가 잘 돌아가는 것을 지켜보는 일은 캠프에 지원하지 않았다면 겪지 못했을 매우 값진 경험이다.

 

아쉬웠던 점은 역시나 경험 부족이다. 필요한 기능을 함수로써 구현하는 과정에서 기능이 늘어나면 늘어날 수록 알고리즘이 복잡해져서, 다중 for문, 다중 if문 등을 사용하는 등 난해한 코딩을 피할 수 없었다. 그래도 코린이 입장에서는 이마저도 구현만 되면 엄청 뿌듯하긴 하더라. 😎

🎈 Now What?

1. 기승전SQL

아무래도 SQL을 다시 봐야할 것 같다. 프로젝트를 진행하면서 DB 관련 부분을 거의 만져보지 못한 탓인지, 슬슬 가물가물하다. "서브쿼리가 뭐였지"라면서... 위의 JOIN부분은 머릿속에서 억지로 끌어내서 글로 쓰는데 시간을 얼마나 잡아먹었는지 모른다. 프로젝트에서 streamlit을 만지면서 코딩은 실습이 중요하다는 것을 절감한 만큼, SQL도 예제를 많이 풀어보면서 내 걸로 만드는 수밖에 없겠다.

2. AI/ML/DL

다음주부터 본격적으로 데이터 분석을 배우는 것으로 알고 있다. 머신러닝과 딥러닝, 코드로써 이들을 구현하는 것 자체는 어렵지 않지만, 데이터(Cross-sectional vs Time Series, Categorical vs Numerical, etc.)에 따라, 연구 목적(Classification vs Regression)에 따라 적절하게 데이터를 정제하고 모델을 선택하고 성능과 결과를 평과하는 것은 매우 전문적인 지식을 요구하는 일이다(분석의 틀이라고들 하던가). 짧은 시간 동안 많은 내용을 다루게 될 텐데, 이번 주말은 내가 지금까지 개인적으로 공부하면서 만들어 둔 통계 및 데이터 과학 자료를 침대 위에 누워서 훑어보는 시간을 가져야겠다.

여담 1

최근 태블릿에서 개발 환경을 세팅해보고 있다. 코딩 테스트 스터디 문제풀이나 회고 작성, 가벼운 코드 수정 등의 간단한 일은 아이패드, 키보드, 마우스만 들고 카페에 가서 하면 가볍고 좋겠다는 생각이 들었다. 그래서 여러 앱을 깔아서 작업들을 해봤다. 확실히 간단한 작업들은 할 만했는데, 터미널을 이용해야 하는 작업에서 제한 사항이 생겨 이 점은 아쉬웠다. 특히 이번 프로젝트에서 활용한 streamlit은 아나콘다 프롬프트에서 streamlit run filname.py라는 명령어로 실행해야 해서 아이패드로는 일절 작업할 수 없었다. 일단 코테 준비 위주로 사용해보고, 앞으로 조금 더 연구해서 많은 기능들을 사용해 봐야겠다.

여담 2

이번 회고 목표는 비움이라고 했지만, 쓰다 보니 하나도 비운 티가 나질 않는다. 분명 많이 줄이고 줄였는데 말이다. 역시 채우는 것보다 비우는 것이 어려운 것 같다 🙄