이번에는 "Hello World"라는 문자열을 콘솔에 출력하는 방법과, 파이썬에서 세미콜론의 용도에 대해서 알아보고자 한다.

Hello World

파이썬에는 글로벌 네임스페이스에서 빌트인 함수들이 여럿 지원된다. List 자료형의 요소를 정렬한 결과를 반환하는 함수도 있고, 요소를 뒤집은(reversed) 결과를 반환하는 함수, 숫자의 절대값을 반환하는 함수, 그리고 파이썬 3.7에서는 디버깅을 위해 break point를 걸어주는 함수도 생겨났다. 콘솔 출력을 위해선 빌트인 함수들 중 print를 사용하며, 파이썬의 빌트인 함수들은 공식 문서에서 확인할 수 있다. 최근부터 한국어 문서화가 잘 진행되고 있어서 편하다.

print 함수의 시그니처는 print(*objects, sep='', end='\n', file=sys.stdout, flush=False)인데, 'Hello World'를 출력하기 위해서는 기본값이 정의되어 있는 sep, end 등의 인자는 아직 신경쓸 필요 없고, *objects 부분만 이해하고 있으면 된다. 파이썬에서는 가변 인자(variable argument)를 위해 * 기호를 쓰므로, 여러 값을 한 번에 출력할 수 있다는 것을 의미한다.

위 두 개의 print문의 결과는 동일하게 'Hello World', 그리고 개행이다. print 함수는 인자로 전달된 objects를 하나씩 출력할 때, sep에 지정되어 있는 문자열이 사이사이에 들어가기 때문에 2번 라인의 결과도 'Hello World'가 된다. sep을 바꾸면 어떻게 될까?

'Hello'와 'World' 사이에 sep인 ', '가 포함되어, 결과는 'Hello, World'가 된다. 참고로, 파이썬은 위처럼 인자를 '선택'하여 값을 전달할 수 있다. 키워드 인자라고 부른다.

파이썬에서의 세미콜론

일부 가이드에서는 파이썬에서 세미콜론은 optional(선택적)이라고 이야기하나, 잘못된 설명이다. '세미콜론이 optional하다'라는 설명은 '써도 되고, 안 써도 된다'라는 이야기이므로 JavaScript에 어울린다.

세미콜론을 붙이던, 붙이지 않던 상관 없다(일부 버전에서는 그렇지 않을 수도 있다). 파이썬에서 세미콜론은 단지 여러 statement를 한 줄에 이어서 사용할 때만 쓸 수 있다.

'프로그래밍 > Python' 카테고리의 다른 글

산술 연산자  (0) 2019.01.28
숫자 자료형과 리터럴  (0) 2019.01.25
파이썬의 타입 시스템과 빌트인 타입, 변수 선언  (0) 2019.01.23
주석  (0) 2019.01.07
개요와 설치  (0) 2019.01.01

필자는 고등학교 1학년 말에 처음으로 백엔드 포지션에서 프로젝트를 진행했다. 내 블로그에서 자주 우려먹는 교내 공식 기숙사 관리 시스템인데(지금 파업 상태다 ㅡㅡ), 같은 동아리였던 선배와 함께 Vert.x라는 서버 프레임워크가 지원하는 Vert.x-Web 툴킷으로 Java 8 런타임에서 돌아가는 웹 어플리케이션 서버를 개발했었다. 지금 생각해보면 참 많은 문제가 있었다.

배포 방식

첫 번째는 배포 방식이다. 테스트 코드가 없는 건 둘째 치더라도, 신규 기능 개발이나 버그 해결을 하고 배포하기까지 사람 손이 정말 많이 들어갔다.

  1. Maven으로 WAS를 .jar 형태로 빌드한다.
  2. WinSCP라는 FTP 클라이언트로 서버에 원격 접속한다.
  3. jar 파일을 덮어쓴다.
  4. Putty라는 SSH 클라이언트로 서버에 원격 접속한다.
  5. screen으로 돌아가고 있는 WAS 세션에 들어가서 서버를 껐다 켠다.

다운타임 문제도 있고, 자동화할 수 있는 부분이었지만 자동화할 생각을 하지 못했다.

물리 서버가 한 대

리눅스에서 물리적인 터미널을 가상 터미널 여러개로 다중화해주는 screen이라는 도구를 써서, screen 하나는 서버 돌리고, 하나는 DB서버 돌리는 식으로 운영했다. 이게 사실 비용적인 문제 때문에 어쩔 수 없긴 했지만, 물리 서버가 한 대인 게 문제임을 인지하지 못했던 것이 더 컸다. '서버가 한 대인데 그게 고장나면?'이라고 누군가 질문하면, '아 그럼 고쳐야죠'라고 대답했을 것 같다. API 서버와 DB 서버를 분리해야 하는 필요성을 느낄만한 경험도 없었고, 아직 혼자 뭔가를 알아보기 어려운 내게 누군가가 가르쳐주지도 않았으니 말이다.

테스트 코드 없음

테스트를 코드가 아니라 PostMan으로 했다. happy path(가장 이상적인 로직)에 대해서 요청 몇 번 해보고, 잘 되면 배포했다. 이런 습관 때문에 문제가 아주 많았는데, 한가지 예를 들면 사용자가 2015년 이전의(파싱된 데이터가 없는 날짜의) 급식 정보를 로드하려 할 때 서버가 500을 내려줬던 적이 있었다. 테스트 코드가 없는 서버를 운영하는 것에 대해서는 말이 더 필요할까 싶다.

브랜칭 모델, 리뷰 없는 배포

먼저, 기능 개발이나 이슈 대응에 대한 별도의 티켓팅 과정이 없었다. 같이 서버를 개발하던 선배와 메신저로 얘기하면서 작업을 분배했고, 나는 할당받은 작업들을 wunderlist라는 to-do list 앱에 적어두고 하나씩 진행했다. 작업은 master 브랜치에서 진행했고, 배포는 리뷰 없이 각자 알아서 진행했다. 아래는 2017년 11월에 적어뒀던 작업 목록이다.

까먹어서 처리하지 못한 이슈가 생길 때도 있고, 우선순위 조절도 매우 힘들었다.

로그 시스템 없음

Vert.x라는 프레임워크에는 미들웨어 개념이 있음에도 불구하고 제대로 된 로그 시스템을 구축하지 않았다. '제대로 된 로그 시스템'이라고 한 이유는, 파일 형태로 요청 로그를 남기기는 했기 때문이다. 아무튼 '로그를 남겨서 뭐하나' 싶었다. 로그 시스템이 없다는 건, 서버에 에러가 나고 있어도 인지하지 못한다는 것이다. 따라서 버그는 '보고 받는' 식으로 발견했다. 사용자가 원하기 전에 먼저 대응해두는 것이 더 좋은 흐름인데 말이다.

raw string 형태의 쿼리

코드를 보면 이해가 될 거다. ORM이 논쟁이 많은 기술이고 뭐고 다 떠나서 ORM을 적용했더라면 저 때 조금은 덜 열불나지 않았을까 싶다. 물론 저 때엔 문자열 결합으로 쿼리 문자열을 만들어내고 나서 '와 진짜 잘만들었다'라고 생각했다.

그 외에도 API 문서가 엑셀로 정리되어 있다거나, 서버가 살아있는지 체크하는 health check가 없다거나, 데이터베이스 백업 안해 뒀다가 쿼리 잘못 날려서 계정 정보 다 날아가거나 하는 것도 있었다. 지금 보면 왜 저랬나 싶은데, 당시엔 그게 당연한 건줄 알았다.

왜?

백엔드를 조금 경험해본 사람이라면 사용자를 어떻게 인증할지, 암호화는 어떻게 할지, API 문서는 어떻게 작성해야할 지 같은 걸 비교적 쉽게 의사결정할 수 있지만, 그렇지 않은 사람은 이런 게 필요하다는 것조차 깨달을 수 있는 경로가 별로 없다.

  1. 어플리케이션 레벨의 경우, 언어와 프레임워크의 선택지가 너무 다양해서 예제를 찾기 힘들다. 안드로이드는 Java나 Kotlin, iOS는 Objective-C나 Swift, 웹 프론트엔드는 React, Vue처럼 그래도 손에 꼽는 수준인데, 백엔드는 그렇지 않다. Java+Vert.x 예제는 정말 찾기 어려웠다. 요즘 갖고 노는 Python+Sanic도 그렇고.
  2. 당연하겠지만, 애초에 production level의 코드를 뜯어볼만한 기회가 별로 없다. 웹이라면 개발자 도구를 켜고, 앱은 종종 GitHub에 올라오는 코드로 얼추 알아볼 수 있을텐데 말이다.
  3. 백엔드는 코드만 봐서 되는 포지션이 아니다. 배포와 배포 패턴, 모니터링, 로깅, 스토리지, 트래픽 변동의 대응처럼 코드 외적으로 신경써야 할 부분이 많다.
  4. 어느 기술이던 경험해보지 않으면 필요성을 못 느끼는 것이 맞다. 그러나 백엔드는 그런 경험조차 해보기 어렵다. 예를 들어, 변동이 적은 데이터는 캐싱해 두면 좋지만, 굳이 캐싱해두지 않아도 데이터베이스에 쿼리하는 시간이 워낙 짧으니 필요성을 못 느낀다. 학교에서 조별 과제로 하는 프로젝트는 사용자가 매우 적거나 없으니 말이다. 로깅도 그렇고, 백업도 그렇고, 데이터베이스 서버 분리도 그렇다. 동기부여의 기회가 별로 없다.

이런 배경에서 어째저째 백엔드 엔지니어라는 이름을 단 채 일하기 시작하고 나니, 이런 걸 도와줄 수 있는 컨텐츠를 만들어보고 싶었다. 필자의 경우엔 뭔가 하나 깨우치는 걸 혼자 하려면 엄청 어려웠는데, 누가 옆에서 5분이라도 설명해 주면 꽤 쉬웠던 것 같아서, 그런 설명충 컨텐츠를 기획했다. 고등학교 곧 졸업하는데 백엔드 좋아하는 후배들한테 뭐 하나 남겨주고 싶기도 했고, 맨날 SQL이나 파이썬 얘기만 하다가 좀 색다른 컨텐츠를 진행해보고 싶기도 했다. 

배포 전에 테스트를 돌리고, 배포를 자동화하고, 로그를 남기고, 시각화하고, 로그 파이프라인 중간에 버퍼나 proxy를 끼워넣는다거나 하는 게 누군가에겐 당연한 일일 수도 있겠지만, 그렇지 않은 사람들이 많다. 그래도 배경지식이 좀 생기면 혼자 찾아볼 수 있는 능력이라도 생기니, 같이 프로젝트 하나 해본다고 생각하면 될 것 같다. 컨텐츠 제목이 좀 싸가지 없어 보일 수도 있는데, 그냥 저게 평소 필자의 말투라서 저렇게 지은거고 딱히 뭐 의미를 부여하고자 한 건 아니다.

이 컨텐츠의 룰

'백엔드가 이정도는 해줘야 함'이라는 컨텐츠는,

  1. 친구 서너명과 '게시판 기반의 커뮤니티 포털' 프로젝트를 진행해서 서비스화 시킨다고 가정한다. 이 배경을 기반으로, 백엔드의 관점에서 기술스택 의사결정부터 시작해 '백엔드가 이정도는 해줘야하지 않나'싶을 때까지 이것저것 붙여볼 것이다.
  2. 의사결정은 '다 ~로 하니까'가 아니라 상황에 맞춰 유동적으로 해볼 것이다. 트렌드를 따르거나, 추측을 기반으로 한 기술 선택은 멍청한 것 같다는 생각이다. '로그 분석에 ELK가 일반적이어서'같이 '당연히 이래야지'같은 이유로 기술을 도입하는 것은 본인에게나 조직에게나 좋지 않은 것 같다. 컨텐츠를 진행하며 계속해서 '내 결정이 옳다는 근거는 무엇인가?'라는 고민을 글로 옮길 것이다.
  3. 프로젝트에서 이런저런 외부 도구들을 쓰기 때문에, 백엔드로 프로젝트에 두세번 정도 참여해 본 사람 정도의 수준에 맞춰서 진행한다. 컨텐츠의 후반으로 갈 수록 이런 경향이 심화될 것이다. git, SQL, HTTP 등을 경험해본 적 없다면 초반 몇 챕터를 읽는 것도 어려울 수 있다. A to Z 모두 설명해보곤 싶지만, 필자의 역량이 부족하기도 하고, 그렇게 들어가기 시작하면 끝이 없기도 하니 이건 각자가 여기저기서 알아보며 공부하도록 하자. 주는대로 받아먹기만 잘하는 사람에겐 어울리지 않는 내용일 것이다.
  4. 조직 내 문서화 툴, 메신저와 같이 '프로젝트 팀 전체'에 해당하는 의사결정은 하지 않을 것이다.
  5. 의사결정의 흐름을 부드럽게 하고, 설명을 더 알차게 하기 위해 되도록 필자가 가장 잘 쓰는 기술을 선택할 것이다. 의사결정에서 '조직에게 가장 익숙하다'라는 이유를 붙이는 경우가 그런 예다. 물론 정말로 '이 기술이 조직에게 익숙한가'는 의사결정의 기준이 되어야 하는 것이 맞지만, 이 컨텐츠에 한해서는 '익숙함'이라는 기준이 의사결정의 우선순위가 조금 더 높을 뿐이라는 것이다.
  6. 모든 의사결정은 배경과 요구사항-선택지-의사결정 순서의 소분류로 나누어 정리할 것이다.

컨텐츠를 생성하는 필자의 배경과 관점 안에서 의사결정이나 코딩을 하게 될테니, 주관적인 부분이 조금 있을 것이다. 그러니 '얘는 프로젝트에서 백엔드를 이렇게 하는구나' 하며 이해해도 될 것 같다. 무작정 싹 다 설명하면서 도와주기보단 자립을 돕는 식으로 글을 쓰도록 노력해 보겠다. 모바일 기준 7~8화면 정도 분량으로 4~50편 정도에 걸쳐 진행될 예정이며 적어도 2019년 말까진 글이 활발히 올라올 것 같다.

컨텐츠를 진행하며 의사결정이 필요할 때마다 '이 방법은 이래서 구리고, 저 방법은 이래서 선택하지 않겠다'같은 이야기를 많이 하게 될텐데, 필자가 '구리다'라고 하는 걸 본인이 썼거나 쓰고 있다고 해서 부끄러워할 필요는 없다. 다 필자의 주관적인 생각이고 '더 나은 방법'은 각자가 고민해야 하는 일이니까, '더 나은 방법이 없을까?'하는 고민을 멈추지만 말자. 그리고 가끔 가다가 예의없이 대충 설명하고 넘어가는 부분이 분명 있을테니, 맘에 안 드는 부분이 있다면 어떤 경로로든 얘기해주기 바란다. 더 좋은 글을 작성하는 데엔 독자의 피드백만한 게 없다.

Python 카테고리를 새로 생성했다. 언어론을 조금 배우며 더 나은 설명이 떠오른 부분도 많았고, 입문자를 너무 고려한 탓에 어려운 개념을 일부 배제하느라 제대로 설명하지 못한 부분도 있었다. Python-New 카테고리는 파이썬에 대해 조금 더 확실하고 명쾌한 설명을 블로그에 올리기 위해 생성하게 됐다. 기존 파이썬 카테고리와 설명이 일부 겹칠 수도 있으나, 적어도 설명이 줄어들진 않을 예정이다.

Java, C# 등 대부분의 주류 프로그래밍 언어들처럼 Python도 C로 구현되었다. Python은 아래와 같은 특징을 가진다.

  • 오픈 소스
  • 인터프리터
  • 별도의 entry point가 없는 스크립트 언어
  • 동적 검사, 암시적 형변환이 없는 강타입, 동적 타이핑
  • 암시적 타입 추론
  • 타입 명시를 지원
  • 멀티 패러다임이면서, class로 객체지향을 표현. 다중 상속 허용
  • 연산자 오버로딩 존재
  • Reference counting과 가비지 컬렉션 기반의 메모리 관리
  • 공식 코드 컨벤션 가이드라인 존재(PEP8)
  • optional한 세미콜론(그러나 자바스크립트 스타일의 optional과는 조금 다름)
  • 대부분의 프로그래밍 언어들처럼, Lexical(정적) scoping
  • function level scoping
  • 의존성 관리와 가상 환경 구성에 대한 지원이 npm만큼 강력

그리고 파이썬의 높은 생산성에 도움을 주는 몇가지 syntatical sugar와 기능들이 존재한다.

  • for statement는 foreach로만 사용 가능
  • 비교 연산자 chaining 가능(a < b < c)
  • switch가 없음
  • 가독성을 위해, 논리 연산자에 기호가 아닌 키워드를 사용(&&를 and로, ||를 or로, !를 not으로)
  • 매직 메소드로 인해 생겨나는 강력한 데이터 모델
  • 매우 큰 라이브러리 생태계
  • Glue language라는 개념을 통해, 타 언어와의 바인딩이 쉬움

문화, 강력한 직관성

내가 파이썬을 좋아하는 이유는, 바로 '가장 아름다운 하나의 답이 존재한다'라는 명제와 코드가 영어 아티클처럼 읽히는 강력한 직관성 때문이다. 높은 생산성과 가독성이 나오는 것도 이 때문이다. 인간의 생각을 코드로 가장 쉽게 표현할 수 있도록, 파이썬은 '쉬운 언어'로 진화해 왔다. PEP8이라는 스타일 가이드에 의해 코드 스타일도 단 하나의 스타일로 진화되어 왔다. JavaScript 진영에서 수많은 코드 스타일이 존재하는 것과 사뭇 다르다. '정해진' 것들이 있기 때문에 선택지는 좁을지 몰라도, 가이드라인만 따르면 코드를 확실하게, 정교하게, 가독성 높게 작성할 수 있다.

또한 언어 자체가 웬만하면 가독성이 높을 수밖에 없게 설계되어 있다. 아래의 코드 스니펫을 보면 파이썬이 얼마나 직관적인지 알 수 있다.

파이썬은 쉽다?

파이썬은 쉬운 게 맞다. 진입장벽이 낮기도 하고, 애초에 '쉬운 언어'를 만들고자 하는 의도로 만들어진 언어기 때문이다. 그러나 잘 쓰기는 어렵다. 예를 들어, 숫자들이 공백으로 구분되어 있는 문자열에서 최소값을 출력하는 코드는 아래와 같이 작성할 수 있다.

이 코드는 아래처럼 개선할 수 있다.

아래와 같은 방법도 있다.

발상의 전환이나, '이 코드가 최선인가'하는 압박이 다른 언어들보다 더 많아서, 번아웃이 오는 빈도도 많다. 따라서 우리가 취해야 할 폼은, 책 하나 다 읽었다고 '다 끝났다'라는 생각을 하지 말아야 하는 것이다. 적어도 프로그래밍 언어를 '다 안다'라고 할 수는 없으니 말이다.

구현체

Python에는 여러 구현체들이 있다. 다른 언어를 하던 프로그래머 입장에서는 'Java 7, 8, 9같은 차이겠거니' 할 수 있다. 그러나 여기서 이야기하고자 하는 건 표준 배포판인 CPython이 C로 개발되는 것과 다르게, 아예 다른 프로그래밍 언어를 이용해 구현된 파이썬이다. 이게 다 파이썬이 오픈소스여서 가능한 일이다. 파이썬 배포판의 대부분도 오픈소스로 개발되고 있다.

CPython

'파이썬'이라고 하면 대부분 CPython을 뜻한다. 파이썬 재단에서 인정하는 표준 구현체이며, python.org에 들어갔을 때 보이는 예제들이나 'Download Python'도 CPython을 기준으로 한다. Python 코드를 바이트코드로 컴파일하고, 그 결과를 VM이 해석하는 방식을 사용한다.

Cython

정적 컴파일러 최적화다. Type mixin이라는 개념을 통해 C나 C++ 모듈로 컴파일한다.

IronPython

.NET FrameworkMono 기반에서 작동할 수 있도록 C#으로 개발된 파이썬이다.

Jython

JVM 기반에서 작동할 수 있도록 Java로 개발된 파이썬이다.

배포판

이것도 다른 언어를 하던 프로그래밍 입장에서는 이해하기 어려울 것이다. 파이썬은 사용하는 대상의 스펙트럼이 매우 넓기에(머신러닝, 웹 서버, 데이터 분석 등) 특정 집단을 타겟으로 하는 별도의 배포판이 존재한다. 데이터 과학을 위한 Anaconda, ActivePython, Enthought Canopy, WinPython 등이 그 예다.

설치

Java 8, 9, 10처럼, 파이썬도 버전 개념이 존재한다. Python 2Python 3가 대표적인 메이저 버전이다. 별다른 이유가 없다면, Python 3을 사용하는 것이 좋다. 애초에 Python 3가 Python 2에서 지적된 여러 단점들을 제거하기 위해 만들어졌기 때문이다. Python 3가 나온 지 얼마 안 됐던 때에는 대부분의 라이브러리가 Python 2만을 지원했었기에 Python 3를 꺼려하는 개발자가 많았지만, 요새는 그런 경우가 거의 없다.

Windows

Windows의 경우에는 공식 페이지인 python.org에서 설치할 수 있다. 별도로 설정을 건드리지 않으면 PATH도 알아서 잡아 주므로 터미널에서 python 명령을 입력했을 때 인터프리터가 정상적으로 동작하면 설치에 성공한 것이다.

$ python

Mac

Mac은 Linux 기반의 운영체제기에, 기본적으로 파이썬이 설치되어 있다. Mac을 비롯한 리눅스, 또는 리눅스 기반 운영체제들은 Python 2와 Python 3를 별도로 관리함에 주의해야 한다. 터미널을 기준으로 하면, Python 2는 python, Python 3는 python3 명령을 사용한다.

$ python
$ python3


'프로그래밍 > Python' 카테고리의 다른 글

산술 연산자  (0) 2019.01.28
숫자 자료형과 리터럴  (0) 2019.01.25
파이썬의 타입 시스템과 빌트인 타입, 변수 선언  (0) 2019.01.23
주석  (0) 2019.01.07
Hello World, 세미콜론은 optional하다?  (0) 2019.01.03

아래 명령은 설치된 모든 패키지를 upgrade합니다.

$ pip install --upgrade $(pip list | egrep -v 'Pack|----' | awk '{print $1}')

아래 명령은 pip, wheel, setuptools를 제외한 패키지를 모두 제거합니다.

$ pip uninstall $(pip list | egrep -v 'Pack|----|pip|wheel|setuptools')

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

requirements.txt로 협업 상황에서의 의존성 관리하기  (0) 2018.11.07
pip  (0) 2018.11.05
__slots__  (0) 2018.11.03
Context Manager  (0) 2018.11.01
Argument Unpacking  (0) 2018.10.30

requirements.txt는 npm의 packages.json과 유사하게, 의존성의 목록을 관리하는 Python의 관례적인 파일입니다. 다수의 개발자가 Python으로 프로젝트를 진행하는 경우, 개발 시 필요한 의존성 패키지들을 동일하게 맞추고 개발하는 것이 좋습니다. Python은 requirements.txt라는 개념을 통해 이를 해결합니다.

pip freeze

pip freeze라는 명령이 있습니다. pip list처럼 시스템에 설치된 파이썬 패키지의 목록을 출력하는데, freeze의 경우 이 목록을 raw하게 표현한다는 특징이 있습니다.

$ pip3 freeze
pbr==4.0.4
six==1.11.0
stevedore==1.28.0
virtualenv==16.0.0
virtualenv-clone==0.3.0
virtualenvwrapper==4.8.2

이전에 pip install을 하면서, 특정 버전의 패키지를 설치할 때 사용했던 표현과 동일합니다. 따라서 최초 개발자 A는 pip freeze 명령어의 결과를 외부 파일로 export해 의존성 목록을 만들 수 있습니다.

$ pip3 freeze > requirements.txt

requirements.txt 파일 내에는 다음과 같이 개발 시 필요한 패키지들이 저장됩니다.

$ cat requirements.txt
pbr==4.0.4
six==1.11.0
stevedore==1.28.0
virtualenv==16.0.0
virtualenv-clone==0.3.0
virtualenvwrapper==4.8.2

파일 이름은 아무렇게나 해도 상관 없으나, 관례 상 위처럼 requirements.txt를 사용하는 것이 좋습니다.

pip install -r [filename]

pip install 명령에는 전달할 수 있는 많은 인자들이 존재하지만, --upgrade와 이 -r 인자를 가장 많이 사용할 것입니다. pip install 뒤에 -r을 붙이고, 의존성 목록이 적혀 있는 파일의 경로를 입력하면 적혀 있는 그대로 패키지들이 설치됩니다.

$ pip3 install -r requirements.txt
Collecting pbr==4.0.4 (from -r requirements.txt (line 1))
https://files.pythonhosted.org/packages/b3/5d/c196041ffdf3e34ba206db6d61d1f893a75e1f3435699ade9bd65e089a3d/pbr-4.0.4-py2.py3-none-any.whl
Collecting six==1.11.0 (from -r requirements.txt (line 2))
https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl
Collecting stevedore==1.28.0 (from -r requirements.txt (line 3))
https://files.pythonhosted.org/packages/17/6b/3b7d6d08b2ab3e5ef09e01c9f7b3b590ee135f289bb94553419e40922c25/stevedore-1.28.0-py2.py3-none-any.whl

(...)

Installing collected packages: pbr, six, stevedore, virtualenv, virtualenv-clone, virtualenvwrapper
Successfully installed pbr-4.0.4 six-1.11.0 stevedore-1.28.0 virtualenv-16.0.0 virtualenv-clone-0.3.0 virtualenvwrapper-4.8.2


'Python 계열 > Python 레거시 글' 카테고리의 다른 글

귀찮음을 줄여주는 간단한 pip 관련 명령 모음(Draft)  (0) 2018.11.12
pip  (0) 2018.11.05
__slots__  (0) 2018.11.03
Context Manager  (0) 2018.11.01
Argument Unpacking  (0) 2018.10.30

좀 잘 나가는 프로그래밍 언어라면, 패키지(라이브러리) 생태계가 굉장히 활발하게 형성되어 있습니다. 그 대표격으로 JavaScript 진영의 npm이 있고, 이와 유사한 패키지 생태계로 Python에는 PyPI(Python Package Index)가 있습니다. 그리고 이들은 pip를 이용해 관리할 수 있습니다. pip를 이용해 외부 라이브러리를 설치하면, 파이썬이 설치된 경로의 site-packages 디렉토리에 적용되어 즉시 사용할 수 있게 됩니다. 별도의 의존성 있는 모듈이나 패키지도 함께 설치해 주기 때문에 매우 편리합니다.

Linux 기반 운영체제에서 pythonpython3 명령이 별도로 존재하는 것처럼, pippip3 명령도 별도로 존재합니다. 각각 python2에 대한 라이브러리, python3에 대한 라이브러리를 관리합니다. pip --version 또는 pip -V를 통해 설치되어 있는 pip의 버전을 확인할 수 있습니다.

$ pip -V
pip 10.0.1 from /usr/local/lib/python2.7/site-packages/pip (python 2.7)
$ pip3 --version
pip 10.0.1 from /usr/local/lib/python3.6/site-packages/pip (python 3.6)

pip install [packages ...]

PyPI에서 packages에 해당하는 패키지들을 가져와 설치합니다. 아래의 명령은 virtualenvwrapper를 설치합니다.

$ pip3 install virtualenvwrapper
Collecting virtualenvwrapper
  Downloading https://files.pythonhosted.org/packages/2b/8c/3192e10913ad945c0f0fcb17e9b2679434a28ad58ee31ce0104cba3b1154/virtualenvwrapper-4.8.2-py2.py3-none-any.whl

(...)

https://files.pythonhosted.org/packages/b3/5d/c196041ffdf3e34ba206db6d61d1f893a75e1f3435699ade9bd65e089a3d/pbr-4.0.4-py2.py3-none-any.whl (98kB)
    100% |████████████████████████████████| 102kB 2.1MB/s
Installing collected packages: six, pbr, stevedore, virtualenv-clone, virtualenvwrapper
Successfully installed pbr-4.0.4 six-1.11.0 stevedore-1.28.0 virtualenv-clone-0.3.0 virtualenvwrapper-4.8.2

여러 개의 패키지들을 한 번에 설치할 수도 있으며, 이미 존재하는 패키지인 경우 'Requirement already satisfied'라는 결과가 보여집니다. 아래의 명령은 시스템에 이미 설치되어 있는 virtualenv와 virtualenvwrapper를 설치합니다.

$ pip3 install virtualenv virtualenvwrapper
Requirement already satisfied: virtualenv in /usr/local/lib/python3.6/site-packages (16.0.0)
Requirement already satisfied: virtualenvwrapper in /usr/local/lib/python3.6/site-packages (4.8.2)
Requirement already satisfied: stevedore in /usr/local/lib/python3.6/site-packages (from virtualenvwrapper) (1.28.0)
Requirement already satisfied: virtualenv-clone in /usr/local/lib/python3.6/site-packages (from virtualenvwrapper) (0.3.0)
Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/site-packages (from stevedore->virtualenvwrapper) (1.11.0)
Requirement already satisfied: pbr!=2.1.0,>=2.0.0 in /usr/local/lib/python3.6/site-packages (from stevedore->virtualenvwrapper) (4.0.4)

다음과 같이 특정 버전의 패키지를 설치할 수도 있습니다. 아래의 명령은 virtualenvwrapper의 4.7 버전을 설치합니다.

$ pip3 install virtualenvwrapper==4.7
Collecting virtualenvwrapper==4.7
  Downloading 

(...)

Installing collected packages: virtualenvwrapper
Successfully installed virtualenvwrapper-4.7.0

pip list

pip list는 pip에 의해, 또는 수동으로 설치되어 있는 패키지들의 목록을 출력합니다.

$ pip3 list
Package           Version
----------------- -------
pbr               4.0.4
pip               10.0.1
setuptools        39.0.1
virtualenv        16.0.0

(...)

wheel             0.31.0

pip install --upgrade [packages ...]

packages에 해당하는 패키지들을 시스템에서 찾아, 설치되어 있으면 최신 버전으로 업그레이드하고, 그렇지 않으면 pip install을 진행합니다. 패키지가 이미 최신 버전으로 설치되어 있다면, 'Requirement already up-to-date'라는 결과가 보여집니다. 아래는 최신 버전인 virtualenv최신 버전이 아닌 virtualenvwrapper를 업그레이드하는 명령입니다.

$ pip3 install --upgrade virtualenv virtualenvwrapper
Requirement already up-to-date: virtualenv in /usr/local/lib/python3.6/site-packages (16.0.0)

(...)

Installing collected packages: virtualenvwrapper
  Found existing installation: virtualenvwrapper 4.7.0
    Uninstalling virtualenvwrapper-4.7.0:
      Successfully uninstalled virtualenvwrapper-4.7.0
Successfully installed virtualenvwrapper-4.8.2

pip uninstall [packages ...]

packages에 해당하는 패키지들을 시스템에서 제거합니다. 시스템에 존재하지 않는 패키지일 경우, 'Skipping *** as it is not installed.'라는 결과가 보여집니다. 아래는 설치되어 있는 jinja2와 markupsafe, 설치되어 있지 않은 flask를 제거하는 명령입니다.

$ pip3 uninstall jinja2 markupsafe flask
Uninstalling Jinja2-2.10:
  Would remove:
    /usr/local/lib/python3.6/site-packages/Jinja2-2.10.dist-info/*
    /usr/local/lib/python3.6/site-packages/jinja2/*
Proceed (y/n)? y
  Successfully uninstalled Jinja2-2.10
Uninstalling MarkupSafe-1.0:
  Would remove:
    /usr/local/lib/python3.6/site-packages/MarkupSafe-1.0.dist-info/*
    /usr/local/lib/python3.6/site-packages/markupsafe/*
Proceed (y/n)? y
  Successfully uninstalled MarkupSafe-1.0
Skipping flask as it is not installed.


Python의 모든 클래스는 인스턴스 속성(instance attribute)을 가집니다. 기본적으로, 이러한 객체의 인스턴스 속성을 관리하기 위해 Python에서 내부적으로 딕셔너리를 사용합니다. 객체의 __dict__ 필드에 접근하여 인스턴스 속성들을 딕셔너리 형태로 확인할 수 있고, 이는 런타임에 새로운 속성을 설정하는 데에 매우 유용하게 사용됩니다.

그러나, 알려진(known) 속성들로 구성된 클래스들의 경우 이러한 구조는 딕셔너리가 낭비하는 RAM 때문에 병목이 발생할 수 있습니다. 클래스 레벨에 __slots__라는 변수를 설정해서, 해당 클래스에 의해 만들어진 객체의 인스턴스 속성 관리에 딕셔너리 대신 속성에 대한 고정된(fixed) set을 사용하도록 할 수 있습니다.

__slots__가 정의된 클래스의 인스턴스에는 __dict__ 필드가 없다 정도밖에 차이가 없어 보이지만, IronPython의 도움을 받아 메모리 사용량을 보면, 효율이 확실히 개선된 것을 확인할 수 있습니다. ipython-memory-usage를 사용해, 1024 * 256개의 객체 생성에 대한 메모리 사용량을 확인해 보았습니다.

In [1]: class WithoutSlots:
   ...:     def __init__(self, name, identifier):
   ...:         self.name = name
   ...:         self.identifier = identifier
   ...:

In [2]: class WithSlots:
   ...:     __slots__ = ['name', 'identifier']
   ...:     def __init__(self, name, identifier):
   ...:         self.name = name
   ...:         self.identifier = identifier
   ...:

In [3]: import ipython_memory_usage.ipython_memory_usage as imu

In [4]: size = 1024 * 256

In [5]: imu.start_watching_memory()
In [5] used 0.0000 MiB RAM in 5.31s, peaked 0.00 MiB above current, total RAM usage 15.57 MiB

In [6]: x = [WithoutSlots(1, 1) for _ in range(size)]
In [6] used 22.6680 MiB RAM in 0.80s, peaked 0.00 MiB above current, total RAM usage 38.24 MiB

In [7]: x = [WithSlots(1, 1) for _ in range(size)]
In [7] used 9.3008 MiB RAM in 0.72s, peaked 0.00 MiB above current, total RAM usage 47.54 MiB


'Python 계열 > Python 레거시 글' 카테고리의 다른 글

requirements.txt로 협업 상황에서의 의존성 관리하기  (0) 2018.11.07
pip  (0) 2018.11.05
Context Manager  (0) 2018.11.01
Argument Unpacking  (0) 2018.10.30
[Python] zip  (0) 2018.07.28

HTTP에서 헤더는 클라이언트와 서버가 요청/응답 본문과 함께 추가 정보를 전달하기 위한 요소다. 요청과 응답에서 모두 사용하는 개념이며, 대소문자를 구분하지 않는 key와 value를 콜론(:)으로 구분하여 헤더를 구성한다. 아래는 HTTP 요청과 응답 메시지의 예인데, 잘 보면 HTTP의 헤더가 많은 부분을 차지하고 있다. 언어, 브라우저 정보, 본문의 길이, 본문의 포맷, 인코딩 정보, 서버 정보 등 주고받을만 한 메타데이터들이 많기 때문이다.

헤더는 그 context에 따라 그룹을 나눌 수 있는데, 다음과 같다.

  • 일반 헤더(General header) : 요청 및 응답에는 적용되지만, 전송되는 데이터와는 관련이 없는 헤더
  • 요청 헤더(Request header) : 가져오고자 하는 리소스, 또는 클라이언트 자체에 대한 자세한 정보가 포함된 헤더
  • 응답 헤더(Response header) : 응답에 대한 추가 정보가 포함된 헤더
  • 엔티티 헤더(Entity header) : 요청이나 응답의 본문에 대한 더 많은 정보가 포함된 헤더

이제 헤더들을 살펴보자. '이런것도 있네' 싶은 헤더들이 종종 있다. 글의 길이가 너무 길어질 것 같아 여러 편에 나누어 구성한다.

Authorization: Basic cGxhbmI6c2VjcmV0

Authorization: <type> <credentials>

Request header로, 사용자의 자격을 증명하기 위해 사용한다. 게시글을 삭제하는 작업을 처리하는 API에서 요청의 Authorization 헤더를 복호화한 뒤 요청자가 리소스에 대한 삭제 권한이 있는지를 확인하는 등의 구현이 있을 수 있다.

type과 credentials를 공백으로 구분하여 value를 구성하며, type은 인증 타입을 나타낸다. BearerBasic, Digest, OAuth 등을 값의 맨 앞에 두는 것을 자주 봤을 것이다. type에 따라 credentials를 구성하는 방식이 달라지므로 서버가 credentials를 복호화하는 방식을 결정할 수 있게 하기 위해서 함께 사용한다. 따라서 credentials는 type에 따라 만들어내는 방식이 달라진다. 예를 들어 Basic 타입은 사용자명과 비밀번호를 콜론을 통해 합친 후(planb:secret), 그 문자열을 base64로 인코딩(cGxhbmI6c2VjcmV0)하여 credentials를 생성한다.

일반적으로, Authorization 헤더가 필요한 곳에 해당 헤더 없이 접근했거나, type이 유효하지 않거나, 복호화가 불가능한 경우 401을, 복호화에 성공했으나 자격 증명에 실패한 경우 403을 상태 코드로 응답한다.

Cache-Control: no-cache

Cache-Control

X- 헤더

'배경지식' 카테고리의 다른 글

ProxySQL  (1) 2019.02.12
직렬화와 JSON  (0) 2019.02.12
ORM  (0) 2019.02.12
HTTP 메소드  (0) 2019.02.12
StatsD  (0) 2019.02.11

Context(문맥)이라는 단어가 참 많은 곳에서 사용되기 때문에 조금 애매하지만, 아무튼 Python의 특정 객체들은 context를 가집니다. 예를 들어, file 객체는 open-close의 2가지 context를 가지는 식입니다.

file 객체의 open과 close처럼, context가 명확한 객체의 경우 Python에서는 원하는 타이밍에 정확하게 리소스를 할당하고 제거할 수 있도록 context manager 프로토콜을 갖도록 하는 것을 권고하고 있습니다. 'context manager 프로토콜을 가지고 있다'의 조건은 생각보다 간단합니다.

  • 매직 메소드 __enter__()를 구현하고 있어야 한다.
  • 매직 메소드 __exit__()을 구현하고 있어야 한다.

이 두 매직 메소드들은 가장 일반적인 context manager인 with-as 문에서 사용됩니다.

with 문에 의해 감싸진 객체는, 블럭이 시작되기 이전에 as 뒤의 객체에 __enter__()의 반환값을 할당하며, 블럭을 탈출하는 시점에 해당 객체의 __exit__()을 호출합니다. file의 경우, __enter__()에서 파일 객체를 열어 반환하고, __exit__()에서 파일 객체를 닫는(close) 역할을 하므로 context manageable하다고 말할 수 있습니다.

Context manageable한 클래스 만들기

__enter__()와 __exit__()이 정의된, context manageable한 클래스를 만들고 with-as 문에 사용해 보겠습니다.

역시, Python은 생각보다 훨씬 깊습니다.

'Python 계열 > Python 레거시 글' 카테고리의 다른 글

pip  (0) 2018.11.05
__slots__  (0) 2018.11.03
Argument Unpacking  (0) 2018.10.30
[Python] zip  (0) 2018.07.28
[Python] Coroutines  (0) 2018.07.27

Python에서 언더스코어처럼, asterisk(*)도 꽤 특별하게 사용할 수 있었습니다. 가장 일반적인 사용 사례는 수 사이의 곱셈과 거듭제곱 연산, Sequence 객체의 반복 확장, 그리고 아래의 가변 인자 표현식이었습니다.

Argument unpacking

Iterable 객체는 unpacking이 가능합니다. 이를 함수 인자에도 적용할 수 있는데, 여기에서 asterisk(*) 기호를 사용합니다.

이를 Argument unpacking이라 부르며, 위치 인자의 argument unpacking에는 List와 Tuple, String과 같은 sequence 객체를 사용할 수 있습니다. 아래는 키워드 인자에 대한 argument unpacking입니다.

여기에는 Dictionary와 같은 mapping 객체를 사용할 수 있습니다.

응용

Argument unpacking은 설정 데이터를 외부에서 다룰 때 유용하게 사용할 수 있는데, 아래와 같습니다. 데이터베이스에 연결하는 스크립트를 작성한다고 가정해 보겠습니다.

위처럼 argument unpacking으로 설정 데이터를 관리하는 경우, 대표적으로 아래 2가지의 강점을 가집니다.

  1. 코드가 간결해집니다. config에서 관리하는 데이터가 많아질수록, 간결함은 더 강해집니다.
  2. 추상화 레벨을 높여 유지 보수에 조금이나마 편의성을 제공합니다. 설정이 추가되거나 제거되는 경우, argument unpacking을 사용하는 코드에서는 원본 해당 객체만 수정함으로써 해결 가능하게 됩니다.


'Python 계열 > Python 레거시 글' 카테고리의 다른 글

__slots__  (0) 2018.11.03
Context Manager  (0) 2018.11.01
[Python] zip  (0) 2018.07.28
[Python] Coroutines  (0) 2018.07.27
[Python] memoize  (0) 2018.07.26

+ Recent posts