project 링크
https://github.com/fclipse/namdo-haksuk-notice
배운점들
1. python crawler 사용법
이 부분은 BeautifulSoup 라이브러리를 사용하여 HTML 문서에서 특정 요소들을 선택하는 코드입니다. 이 코드는 div 태그 중 클래스가 board-list-wrap인 요소 내의 tbody 태그 안에 있는 모든 tr 태그들을 선택합니다.
클래스와 ID 선택자를 BeautifulSoup에서 사용하려면 CSS 선택자를 사용하면 됩니다. 예를 들어, 클래스 선택자는 .을 사용하고, ID 선택자는 #을 사용합니다.
예시:
- 클래스 선택자 사용:
# 클래스가 'example-class'인 모든 요소 선택
elements = soup.select('.example-class')
- ID 선택자 사용:
# ID가 'example-id'인 요소 선택 element = soup.select('#example-id')
- 클래스와 ID를 함께 사용:
# 클래스가 'example-class'이고 ID가 'example-id'인 요소 선택 element = soup.select('.example-class#example-id')
적용 예시:
# 클래스가 'board-list-wrap'인 div 내의 tbody 안에 있는 모든 tr 태그 선택
notice_list = soup.select('div.board-list-wrap tbody tr')
# 클래스가 'example-class'인 모든 요소 선택
example_class_elements = soup.select('.example-class')
# ID가 'example-id'인 요소 선택
example_id_element = soup.select('#example-id')
# 클래스가 'example-class'이고 ID가 'example-id'인 요소 선택
example_class_id_element = soup.select('.example-class#example-id')
이렇게 하면 BeautifulSoup을 사용하여 HTML 문서에서 특정 클래스나 ID를 가진 요소들을 선택할 수 있습니다.
2. python에서 .env파일 변수를 안전하게 불러오는 패턴
DEBUG = os.getenv("DEBUG", "False").lower() in ("true", "1", "t")
이렇게 하면 .env에 DEBUG 변수가 없으면 기본적으로 False값을, True / true / 1 / t 중 하나라도 있으면 True값을 DEBUG 변수에 저장하게 된다.
DEBUG 변수가 bool type 임을 보장해주는데, 정말 식이 간결하게 쓰여진 것 같다.
3. discord 봇 사용법
https://cjwoov.tistory.com/104
해당 블로그를 많이 참고했다.
만약 봇이 작동이 잘 안되면
- 채널 id 확인
- bot previlliged 권한 확인
- oauth2 page에서 bot 권한 확인
을 하자.
나는 잘 안돼서 그냥 3.에서 bot한테 admin 권한을 줬다.
디버깅 내용
- .env 안바뀌는 문제
- 상황: venv 실행중
- 문제: .env파일의 내용을 바꿔도 python을 실행하면 바뀌지 않은 채로 실행됨
- 해결: venv를 종료 -> .env 수정 -> 다시 venv접속 후 실행 -> 해결;;;;
venv를 킨 상태에서 .env를 바꾸면 바로 반영이 안 되는 것 같다.
왜 이러는진 모르겠지만(.venv로 이름을 설정하니까 vscode venv 가 자동실행됨. 이것 때문인가)
.env를 수정했다면 terminal을 종료하고 다시 켜서 앱을 실행하자.
- docker 이미지 관련 문제
- docker 이미지 빌드시 apt-get 못 찾는 문제
-> claude 답변
이 에러는 Docker 이미지 빌드 중 Nginx 설치 과정에서 발생한 문제입니다. 주요 원인은 다음과 같습니다:
nginx.conf 파일 충돌
CopyConfiguration file '/etc/nginx/nginx.conf'
==> File on system created by you or by a script.
==> File also in package provided by package maintainer.
이미 nginx.conf 파일이 존재하는데 새로운 설치가 시도되어 충돌이 발생
해결 방법은 Dockerfile을 다음과 같이 수정하면 됩니다:
dockerfileCopy# Nginx 설치 전에 기존 nginx.conf 제거
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y nginx && \
rm -f /etc/nginx/nginx.conf
# 그 후 nginx.conf 복사
COPY nginx.conf /etc/nginx/nginx.conf
주요 변경사항:
DEBIAN_FRONTEND=noninteractive 추가: 설치 중 대화형 프롬프트 방지
nginx.conf 파일을 먼저 제거하고 나중에 복사
각 명령어를 &&로 연결하여 레이어 수 최소화
이렇게 수정하면 nginx 설치 과정에서 발생하는 설정 파일 충돌 문제를 해결할 수 있습니다.
# Nginx 설치 및 설정
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y nginx && \
rm -f /etc/nginx/nginx.conf
- start.sh 못 찾는 문제
다음 링크 참고
https://kkkkang1009.tistory.com/entry/docker-error-start-not-found
- uvicorn run 관련
https://jandari91.github.io/posts/fastapi_start/
- python main.py 하고 name == 'main' 밑에 uvicorn.run() 적는거랑 cmd에 uvicorn main:app ~~ 적는거랑 같음!!
- nginx conf 관련
``` - proxy_pass http://127.0.0.1:8001; 여긴 localhost가 들어가야 한다.
``` - port 관련
docker -> nginx -> python app 이렇게 3개의 앱이 실행되는데, 이때 사용되는 포트는 총 3가지이다.
1) docker host port - 외부에서 실행 중인 docker 이미지에 접근할 때 쓰이는 포트 / docker run 시 설정된다. 5000:8000 이라면 앞이 host port, 뒤가 container port
2) container port - docker 이미지 내부 포트 / docker run시 설정하고, nginx conf에서 같이 설정해줘야 한다.
3) python app port - python app이 docker image 내에서 돌아갈 때 쓰이는 포트, nginx conf에 설정되며 python app 내 uvicorn.run() or uvicorn main:app 등의 명령어 에서 설정된다. 마찬가지로 같게 맞춰줘야 한다. - fastapi 비동기 처리 관련
이번 프로젝트에서 까다로웠던 점은 다음과 같다.
먼저 discord_bot.py, web_ui.py, scrape.py가 모두 따로따로 노는데 이를 모두 비동기처리로 처리해야 한다.
그리고, discord_bot.py가 실행된 후에 (bot이 준비된 후에) scrapping이 실행되어야 한다.
이 조건을 만족하기 위해선 모두 다 async.gather()에 집어넣어 돌리거나, 각각을 await처리해주는 게 아닌, 따로 함수를 만들고 그 안에서 조건을 걸어 실행해줘야 한다.
나는 다음과 같이 설정해줬다.
async def start_bot():
await bot.start(Config.DISCORD_TOKEN)
async def run_tasks():
# bot이 준비될 때까지 대기 - 그 이전에 공지사항을 확인해버리면 봇이 준비되기 전에 메시지를 보내게 됨
await bot.wait_until_ready()
await check_notices()
config = uvicorn.Config(app, host="0.0.0.0", port=8001, log_level="info", reload=RELOAD)
server = uvicorn.Server(config)
await asyncio.gather(
start_bot(),
server.serve(),
run_tasks()
)
이렇게 하면 start_bot, 서버, run_tasks가 모두 동시에 실행되는데, run_tasks() 안에 chekc_notices는 await이 걸려 있어 bot이 준비되기 전까지 실행되지 않는다.
후기
비동기 처리가 왜 어렵다고 하는지 알 수 있었다.
그리고 생각보다 docker image 및 python run 설정에 시간이 많이 소요했다. (3~4시간)
나중에 기업용 소프트웨어는 더 복잡한 구조의 애플리케이션을 돌려야 할 건데, 그 돌아가는 순서에도 정말 복잡한 과정이 있음을 간접적으로 느꼈다.
그리고 왜 devops 엔지니어를 따로 두는지 알 수 있었다.
+ uvicorn.run()이랑 uvicorn main:app 이것때문에 얼마나 시간을 잡아먹은건지..
아 맞다, 그리고 vscode에서 copilot으론 gpt 대신 claude를 꼭 이용하자... 성능 차이가...
그리고 명심하자. AI는 아직 완벽하지 않다.
자동화 하나 만드는 과정에서도 아직까지 상당히 오류나 버그가 많았다. 다행히 아직까진 대체되지 않을 수 있을 것 같다는 느낌을 받았다.