python을 처음 공부한 이래, 가상환경을 세팅하는 방법으로 conda밖에 없는 줄 알았다.

하지만 그동안 conda로 가상환경을 세팅하면서 자잘한 버그들로 불편했는데 (특히 MacOS M1과의 호환성이 정말 별로였다.)

다음 두 가지 큰 이유로 이번 기회에 다른 방식으로 넘어가게 되었다.

1. langchain 관련 dependency 문제:

- 우선 conda에 langchain 생태계가 없어서 pip로 일일히 다운받아야 했는데, dependency를 챙기는게 꽤나 번거로웠다.

- conda로 가상환경을 세팅하는 장점을 전혀 누릴 수가 없었다.

 

2. poetry를 통한 개발환경 세팅시 원인 모를 에러 거듭 발생

- 이번에 data platform팀 환경 위에 python 개발할 일이 있었는데, 로컬에서 pyspark을 실행하기 위한 가이드대로 해도 계속 에러가 났다.

- python version과 whl 파일 간의 충돌이 계속 발생했는데 아무리 구글링해도 해결되지 않았다.

- 찾아보니 conda 자체가 data science 분야에만 특화되어있기도 하고, 개발 환경에는 적합하지 않은 것 같아 로컬에 설치된 conda를 전부 제거하고 pyenv로 갈아타게되었다.

- conda를 쓰면 안되는 이유가 설명된 블로그: https://devbull.xyz/python-create-environment/ 

 

넘어가면서 거의 몇 시간을 삽질했기 때문에, 까먹지 않고자 남겨두고자 한다.

 

결론

- brew -> pyenv -> pipx -> poetry 순서로 삭제했다 재설치

- conda는 아예 삭제

 

1. python 가상환경은 왜 필요한가?

- python도 일종의 실행가능한 프로그램이다.

- 여러 library들이 이 프로그램에서 사용될 수 있도록 개발되는데, library들끼리 서로 가져다쓰기 때문에 dependency라는게 생긴다.

- 예를 들어 library B가 python 3.8에서 사용할 수 있도록 개발되고 library A의 ver 1을 사용한다고 하자.

- 만약 library A의 ver 2가 배포됐을 때, 사용자는 무턱되고 A, B의 최신 버전을 사용할 수가 없다.

- 아직 B는 A의 ver 1을 사용하고 있기 때문에 B를 설치하면서 A의 ver 1을 함께 설치해줘야 한다.

- 만약 다른 python 프로젝트에서 library C를 사용하고 이 C는 library A의 ver 2를 사용한다면?

- 이런 골치아픈 dependency 문제를 해결하기 위해 각각의 프로젝트마다 가상환경을 세팅한다.

- 그리고 이 가상환경마다 dependency 충돌이 안 일어나도록 일정의 config를 설정해둔다. 

    - poetry가 이런 솔루션 중 하나인데, 복잡한 사용법을 익혀야 한다.

    - 단순하게는 requirements.txt 파일을 만들어서 해당 프로젝트에 필요한 library들과 version을 관리한다.

 

2. pyenv

- 가상환경을 세팅하는 여러 툴이 있다.

- venv, virtualenv, pyenv, conda

- 각 장단점은 검색하거나 ChatGPT에 물어보면 잘 알 수 있으니 생략하고,

- 결론적으로 pyenv로 가상환경을 세팅하기로 했다.

- pyenv는 여러 명령어를 제공한다. (참고: https://shawn-dev.oopy.io/fe911cba-0b25-474c-9c3c-d18fd945c8b0)

- 가장 편하게 느껴진 점은 이것

    - python을 여러 버전으로 설치할 수 있다. (3.9, 3.10, 3.11)

    - 가상환경을 만들 때 설치한 python으로 세팅할 수 있고, 가상환경을 활성화 하는 것도 쉽다 (pyenv activate {가상환경 이름})

    - (핵심) python이 설치된 경로를 복잡하게 챙길 필요가 없다.

 

3. 원인 모를 문제들

- .whl 파일이 os가 지원하는 것과 다른 library가 요구하는 게 다르다는 에러가 거듭 났었다.

- 결국 해결 못해서 brew를 아예 삭제하고 다시 설치하니 해결되었다.

- 이 과정에서 conda로 설치한 python 경로와 pyenv로 설치한 python 경로가 매우 복잡하게 되어있는 것을 보고 한쪽으로 정리해야겠다고 결심했다.

- conda는 아예 삭제해버리고, pyenv도 삭제했다가 다시 설치했다.

public repo에 aws access key 값이 하드코딩된 파일을 커밋했다.

git rebase -i 를 활용하면, commit history 를 지울 수 있다

 

1. 원하는 commit으로 이동 (뒤에 ~을 안 쓰면, 그 이후 커밋만 보임

git rebase -i <commit 해시값>~

 

2. commit history 파일에서 해당하는 commit의 flag를 pick에서 edit으로 변경 -> 저장 후 파일 닫기

3. 하드 코딩된 부분 수정

4. git commit -a --amend

git add --all
git commit -a --amend

5. 수정사항 최종 반영

git rebase --continue

6. 리모트 repo에 push

git push -f

 

해당 커밋에서 하드코딩된 부분이 변경된 채 히스토리에 남음

자꾸 헷갈려서 각 case 별 방법 정리

 

1. Remote Branch에 있는 Branch로 이동

git fetch
git checkout other-branch

git fetch는 메타 정보만 불러옴

 

2. Remote Branch Update 내역 Local Branch에 적용

git checkout other-branch
git pull

업데이트하려는 branch에서 git pull을 하면 Remote Branch에서의 업데이트 내역이 반영됨

 

3. Remote Origin Update 내역 Local Origin에 적용

git checkout other-branch
git pull origin main

업데이트하려는 branch에 main branch의 update 내역이 반영됨

 

4. Local Branch -> Remote Branch

git checkout other-branch
# 작업...
git add --all
git commit -m "comment"
git pull # 내가 commit 하기 전에 다른 사람이 remote branch에 commit했을 경우
git push origin other-branch

5. Local Branch -> Remote Origin Branch (PR로 보통 branch 충돌을 점검해야 하므로, 거의 쓸일 없을 듯)

git checkout other-branch
# 작업...
git add --all
git commit -m "comment"
git pull origin main # 내가 commit 하기 전에 다른 사람이 remote branch에 commit했을 경우
git push origin other-branch:main # git push origin <local>:<remote>

 

Reference

 

Git - ( local / remote ) branch 사용법 정리

Git 명령어 Fetch -> 리모트 저장소에 있는 모든 데이터를 로컬로 가져옴. Git branch [브랜치명] => 새로운 브랜치 생성 Git checkout [브랜치명] => 브랜치 checkout(다른 브랜치로 이동) Git commit => ———> 한

jw910911.tistory.com

 

Git - git-push Documentation

In general, URLs contain information about the transport protocol, the address of the remote server, and the path to the repository. Depending on the transport protocol, some of this information may be absent. Git supports ssh, git, http, and https protoco

git-scm.com

1. 최근 10개 commit들 불러오기 (git-rebase-todo라는 text 파일이 열림)

git rebase -i HEAD~10

git rebase -i 에 대한 설명

-i, --interactive     let the user edit the list of commits to rebase

 

2. git-rebase-todo에서 PR 화면에 안 보이게 하고싶은 commit들 fixup 처리

fixup에 대한 설명

# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message

 

3. git push --force로 다듬은 commit들로 push하면 PR이 다듬어짐

git push origin local:remote --force

 

배경

회사에서 새로운 Airflow Operator를 제작할 필요가 생겼다. 기존에 있는 Operator (Parent Operator)와 거의 유사한 동작을 하기 때문에, 상속을 통해 새로운 Operator(Child Operator)를 만들기로 결정했다. 새로 Child Operator는 다음 두 가지가 Parent Operator와 다르다.

  1. Parent Operator의 Instance 생성시 반드시 필요한 Parameter 중 일부가 Child Operator에서는 필요 없다.
  2. 가장 바깥에서 호출되는 execute Method는 Child Operator에서 동일하지만, execute 내부에서 사용하는 internal Method는 Parent와 다르다.

2가지 사항을 어떻게 Child Operator에 구현할 수 있는지 확인하자.

 

실험

코드

class Parent():
    def __init__(self, name: str, age: int, for_what: str) -> None:
        self.name = name
        self.age = age
        self.for_what = for_what
    
    def execute(self) -> None:
        message = self._internal_method()
        print(f"My name is {self.name}")
        print(message)
    
    def _internal_method(self) -> str:
        return "PARENT IS SUPER!"


class Child(Parent):
    def __init__(self, parent_name: str, **kwargs):
        super().__init__(for_what="", **kwargs)
        self.parent_name = parent_name
    
    def _internal_method(self) -> str:
        return "CHILD IS OVERRIDER!"


if __name__ == "__main__":
    c = Child(name = "HB", parent_name="Lee", age=27)
    c.execute()

 

실행 결과

$ python class-inheritance/override_practice.py
>> My name is HB
>> CHILD IS OVERRIDER!

 

결론

  1. super().__init__()에서 기존 Parent를 선언하는데 반드시 필요했던 parameter for_what에 ""를 집어넣었다.
    • 이게 효과적인 방법일지는 의문 - Python 자체에 class 상속후, 필수 parameter를 변경하는 방법은 없는 듯 하다
  2. "CHILD IS OVERRIDER!"가 출력됐다는 뜻은, execute에서 사용하는 _internal_method()가 override된 Child의 Method를 사용했다는 뜻이다.

 

export로 환경변수 선언하면, 로그아웃 or 세션종료시 환경변수가 초기화되어 있음

 

mac에서는 .zshrc, linux에서는 .bashrc에 추가해주어야 함

 

$ vi ~/.bashrc

export ENV_VAL=HELLO

$ source ~/.bashrc
$ echo $ENV_VAL

내 로컬 컴퓨터로 작업하다 회사 aws s3에 접근할 일이 생김

Reference

https://stackoverflow.com/questions/49716583/how-to-temporarily-switch-profiles-for-aws-cli

 

How to temporarily switch profiles for AWS CLI?

Updated answer (7/10/2021): For AWS CLI v1, do this: export AWS_DEFAULT_PROFILE=user2 For AWS CLI v2, the following will work: export AWS_PROFILE=user2 The full question is below for context: (1.)

stackoverflow.com

 

나는 aws v1을 사용 중이어서 다음 명령어 실행함

export AWS_DEFAULT_PROFILE=company_name

aws s3 ls로 어떤 계정에 로그인되어있는지 확인 가능


aws configure list: 지금 profile 정보 보여줌

credentials란 여러 profile의 인증 정보를 담은 것

configuration는 profile의 구성

HTTP 프로토콜이란?

강의영상

https://www.youtube.com/watch?v=TwsQX1AnWJU&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=28 

 

7계층 프로토콜은 매우 많다 (socket 통신 프로그래밍이 7계층 프로토콜을 만드는 것)

그 중 HTTP에 대해 배울 것 (Web에 사용하는 프로토콜)

 

목차

  1. HTTP 프로토콜
  2. HTTP 요청 프로토콜
  3. HTTP 응답 프로토콜
  4. HTTP 헤더 포맷
  5. 실습

 

HTTP 프로토콜

웹을 만드는 기술들: HTTP, HTML, Javascript, CSS, ASP, JSP, PHP, DB 등등

  • HTML, Javascript, CSS: 클라이언트 사이드에서 동작하는 코드들 (저장은 서버쪽에 되어 있다)
    • 클라이언트는 웹 브라우저로 다운 받는다
  • HTTP: ㄴ를 다운로드 받는 프로토콜
  • HTTPS: HTTP에 보안적인 요소를 추가
  • ASP/ASP.NET, JSP, PHP, Python: 서버 사이드에서 실행되는 코드 (백엔드: 클라이언트한테는 안 보임)

 

HTTP 프로토콜의 특징

  • HyerText Transfer Protocol
  • www에서 쓰이는 핵심 프로토콜로 문서의 전송을 위해 쓰이며, 오늘날 거의 모든 웹 어플리케이션에서 사용되고 있다.
    • 음성, 화상 등 여러 종류의 데이터를 MIME로 정의하여 전송 가능
    • HTTP 특징
  •     - Request / Response (요청/응답) 동작에 기반하여 서비스 제공

 

HTTP 1.0의 특징

  • 연결 수립 -> 동작 -> 연결 해제의 단순함이 특징
    • 하나의 URL은 하나의 TCP 연결
  • HTML문서를 전송 받은 뒤 연결을 끊고 다시 연결하여 데이터를 전송

 

HTTP 1.0의 문제점

  • 단순 동작 (연결 수립 -> 동작 -> 연결 해제)이 반복되어 통신 부하 문제 발생

 

HTTP 1.1의 특징

  • HTTP 1.0과 호환 가능
  • Multiple Request 처리가 가능하여 Client의 Request가 많을 경우 연속적인 응답 제공 -> Pipeline 방식의 Request / Response 진행
  • HTTP 1.0과는 달리 서버가 갖는 하나의 IP 주소와 다수의 Web Site 연결 가능

HTTP 1.1의 장점

  • 빠른 속도와 Internet Protocol 설계에 최적화될 수 있도록 캐시 사용
  • Data 를 압축해서 전달이 가능하도록 하여 전달하는 Data 양이 감소

 

HTTP 메소드

강의영상

https://www.youtube.com/watch?v=rxaBwwI_JnI&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=29 

 

HTTP 요청 프로토콜의 구조

16진수가 아닌 영어로 작성
Request와 Headers

 

- Request Line이 중요

Request Line의 구조

요청 타입과 URI가 매우 중요

 

HTTP 메소드 (요청 방식)

GET과 POST가 매우 중요 (나머지는 보안상 막아둠: 클라이언트가 서버쪽 데이터를 수정하면 안됨)

  • 애매한 부분: GET으로도 데이터 요청 할 수 있고, POST로도 데이터를 받을 수 있다
  • 둘의 차이는 무엇?
    • GET은 데이터를 URI에 포함시켜 보냄 (ex. ~~웹툰을 보고싶다)
    • POST는 URI에 포함시키는게 아닌, Body에 포함시켜 보냄 (ex. 중요한 데이터: 로그인 ID, PW)

URL, URI란?

강의영상

https://www.youtube.com/watch?v=2ikhZ_fNP5Y&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=30 

URI의 구조

URI(Uniform Resource Identifier): 인터넷 상에서 특정 자원(파일)을 나타내는 유일한 주소

list.nhn이라는 파일(프로그램)에 titleId=570503&weekday=thu를 보내는 구조

구조

  • scheme://host[:port][/path][?query]

ex)

ftp://IP주소:포트/파일이름

http://IP주소:포트/폴더이름/파일이름 or http://도메인주소/폴더이름/파일이름

* IP주소를 도메인주소로 바꿔주는 건 DNS서버가 하는 일

* 포트는 웹 브라우저가 80번, 443번을 알아서 사용 (작성 생략해도 됨)

 

실습

강의영상

https://www.youtube.com/watch?v=XbGJYsxed2w&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=31 

 

URI 이해를 위한 실습

강의영상

https://www.youtube.com/watch?v=HBojczyd1Ac&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=32 

환경변수: 시스템의 환경과 관련된 값을 저장하는 변수 ex. PATH

    - java 기반의 프로그램을 실행하려면, javaw.exe를 사용해야 한다

 

URI

- 해당 경로를 통해, 클라이언트들이 서버 파일에 접근

 

query (?다음에 쓰는 값들)

- 해당 경로에 있는 파일(프로그램)에 특정 값들을 넘겨줌

 

HTTP 응답 프로토콜

강의영상

https://www.youtube.com/watch?v=kuucNF4Zvbs&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=33 

 

HTTP 응답 프로토콜 구조

요청 프로토콜과 유사 (Status Line은 다름)

Body에 사용자가 요청한 데이터가 들어감

 

Status Line 구조

상태코드 + 상태 문구는 하나의 쌍

상태코드

: 서버가 알려주는 여러가지 정보

  • 200: 정상적으로 완료 (상태문구: OK)
  • 400~: 에러 - 클라이언트 잘못
    • 403: 클라이언트가 권한이 없는 페이지를 요청했을 때 (상태문구: Forbidden)
    • 404: 클라이언트가 서버에 없는 페이지를 요청했을 때 (상태문구: Not Found)
  • 500~: 에러 - 서버 잘못 (프로그램 오류, 코드 버그 등)
    • 500: 서버의 일부가 멈췄거나 설정 오류가 발생 (상태문구: Internal Server Error)
    • 503: 최대 세션 수를 초과했을 때 (상태문구: Service Unavailable)

 

 

HTTP 헤더

강의영상

https://www.youtube.com/watch?v=mQTGmxendk8&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=34 

HTTP 헤더 구조

HTTP 헤더는 종류가 정말 많다

 

일반헤더

  • 일반적인 정보를 담고 있음
  • 종류
    • Content-Length: 메시지 바디 길이를 나타날 때 씀
    • Content-Type: 메시지 바디에 들어있는 콘텐츠 종류 (ex. HTML 문서는 text/html)

 

요청 헤더

  • 클라이언트 정보를 담고 있음
  • 종류
    • Cookie: 서버로부터 받은 쿠키를 다시 서버에게 보내주는 역할
    • Host: 요청된 URL에 나타난 호스트명을 상세하게 표시 (HTTP 1.1은 필수로 보내야 함)
    • User-Agent: 클라이언트 프로그램에 대한 식별 가능 정보를 제공 => 핸드폰, PC로 접속했는지 등을 알 수 있음

 

응답 헤더

  • 서버 정보를 담고 있음
  • 종류
    • Server: 사용하고 있는 웹서버의 소프트웨어에 대한 정보를 포함
    • Set-Cookie: 쿠키를 생성하고 브라우저에 보낼 때 사용. 해당 쿠키 값을 브라우저가 서버에게 다시 보낼 때 사용

 

실습

강의영상

https://www.youtube.com/watch?v=dhMrKTwNI8U&list=PL0d8NnikouEWcF1jJueLdjRIC4HsUlULi&index=35 

 

burpsuite라는 프로그램 사용

: 요청 / 응답 중간에 프로토콜을 잡아서 수정가능

 

코드 수정 => 화면에 뜨는 숫자를 바꿀 수 있음 (클라이언트 화면에만 바뀌어 있음)

 

+ Recent posts