728x90

WAF IAM Policy 에 관한 이야기

AWS 를 사용하면서 만나는 Global 서비스들이 여럿 있습니다. CDN 제품인 CloudFront 가 가장 대표적인 Global 서비스이고 CF 에서 사용할 수 있는 WAF (Web Application Firewall) 역시 Global 서비스 입니다. 이런 제품들의 특징은 사용자에게 가까운 곳에서 동작해야 효과가 좋다(?)는 점이죠.

최근 WAF 는 Version 2 를 출시하면서 기존의 WAF 는 WAF Classic 으로 부르기 시작했습니다. 몇 가지 변화들이 있지만 그 이야기를 하려는 것은 아니구요, 새로운 버전이 출시되면서 IAM Policy 에도 WAF v2 에 대응하는 부분이 추가되었고 Console 접근을 하려다 보니 생겼던 에피소드를 IAM Policy 를 살펴보면서 간단히 이야기 해볼까 합니다. 


IAM 에서 미리 정의하여 제공되는 Policy 중 WAF 와 관련된 것은 두가지가 있습니다. 하나는 AWSWAFFullAccess 이고 다른 하나는 AWSWAFConsoleFullAccess 입니다. 이들 Policy 를 사용하여 권한을 부여해 두었다면 Action 항목에 "wafv2:*" 가 자동으로 추가, 적용이 되었고 WAFv2 의 사용에 문제가 없어야 합니다. 개별 정책을 JSON 으로 정의했다면 명시적으로 "wafv2" 를 넣어줘야 합니다. 

# Policy : AWSWAFFullAccess

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "waf:*",
        "waf-regional:*",
        "wafv2:*",
        "elasticloadbalancing:SetWebACL",
        "apigateway:SetWebACL"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

문제는 기존 WAF Classic 은 화면 진입은 잘 되면서 에러 메세지가 눈에 안 띄는 곳에 나오면서 콘솔 진입시 위 정책이 충분치 않다는 것을 인지하기가 조금 어려웠습니다. 그런데 WAF v2 에서는 Unauthorized 메세지가 대박 크게 나오면서 마치 wafv2 정책이 제대로 들어가지 않은 것처럼 보이는 제보가 들어왔던 것이지요. 결론을 먼저 말하면 위 정책으로는 WAF Classic 이든 WAFv2 든 제대로 동작하지 않는게 정상입니다. 

# Policy : AWSWAFConsoleFullAccess

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "apigateway:GET",
        "apigateway:SetWebACL",
        "cloudfront:ListDistributions",
        "cloudfront:ListDistributionsByWebACLId",
        "cloudfront:UpdateDistribution",
        "cloudwatch:GetMetricData",
        "cloudwatch:GetMetricStatistics",
        "cloudwatch:ListMetrics",
        "ec2:DescribeRegions",
        "elasticloadbalancing:DescribeLoadBalancers",
        "elasticloadbalancing:SetWebACL",
        "waf-regional:*",
        "waf:*",
        "wafv2:*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

AWSWAFConsoleFullAccess 정책을 살펴보면 AWSWAFFullAccess 에 있는 정책들이 모두 들어가 있습니다. 추가로 WAF 적용의 주된 대상이 되는 API Gateway, CloudFront, ELB 관련한 항목들이 추가된 것이 보이는데요, 이들 정책이 있어야 AWS Console 화면 구성에 필요한 정보들을 가져올 수 있고 화면 접근시 Authorization 에러가 발생하지 않게 되는 것으로 생각됩니다. 한참을 헤메다 요 정책을 그룹에 추가해 주고나서 문제를 해소할 수 있었습니다. 


AWS 를 쓰다보면 IAM 에서 적절하고 충분한 권한을 주는 문제에 종종 부딪히게 됩니다. 적용된 정책을 확인하는 페이지도 제공하고 있긴 하지만 실제로 사용자들이 API 호출이나 Console 액세스시에 겪게 되는 문제와 연결고리를 찾기 힘든 경우도 종종 생깁니다. 결국은 경험치가 있어야 하는 것인가 하는 고민에 빠지게 되는 5월의 어느날입니다. 

 

[ AWS 자격증을 준비하신다면 - 3만명이 수강한 강의 추천드립니다! ]

 

Ultimate AWS Certified Solutions Architect Associate (SAA)

Pass the AWS Certified Solutions Architect Certification SAA-C02. Complete AWS Certified Solutions Architect Associate!

www.udemy.com

[ WAF Classic 과 WAF v2 의 비교는 아래 블로그가 깔끔합니다! ]

 

 

AWS WAF Version 2 소개(개선 사항)

오늘은 새롭게 출시된 AWS WAF Version 2 에서 제공하는 새로운 기능들과 기존 AWS WAF(Classic) 대비 개선된 사항들에 대해 살펴보도록 하겠습니다.

medium.com

 

본 포스팅은 소정의 수수료를 받을 수 있습니다.

728x90
728x90

어떤 언어를 사용하던 문자열 포맷팅은 꽤 짜증스러운 부분입니다. 화면에 출력을 하던 파일에 내용을 쓰던 다양한 문자열 포맷팅을 사용해야만 합니다. 문자열 포맷팅이 데이터베이스와 같이 민감한 요소를 액세스 하는데 사용된다면 (가령 쿼리 문장 같은...) 포맷팅 이전에 SQL Injection 과 같은 악의적인 공격도 감안해야 하기 때문에 더 신경을 써야만 합니다. 

자바스크립트를 오랫동안 사용하면서 유레카를 외쳤던 때가 있었습니다. 물론 자바스크립트도 계속 진화하고 있기 때문에 Wow 한 순간들이 여러번 있었지만, 그 중에서도 가장 기억에 남는 것은 ES6 에서 도입된 템플릿 문자열이었습니다. 일반적으로 변수를 선언하거나 문자열을 만드는 과정에 백틱(`)을 이용해 변수나 메서드를 변수에 직접 바인딩 할 수 있게 해주는 방법이었습니다. 

// Javascript ES6 에 도입된 템플릿 문자열
console.log(`Default ENV value is ${process.env.MY_VALUE}`);

파이썬으로 코드를 만들면서도 <당연히> 문자열을 핸들링 해야 하는 상황이 계속 발생했습니다. 디버깅 로그에서부터 사용자 콘솔 화면에 뿌려줄 응답 결과, 데이터베이스 쿼리문에 까지 당연한 일이었습니다. 오래전 방식으로 문자열을 + 로 연결하는 작업을 하다보니  `ES6 에서의 템플릿 문자열 같은게 없을까?`하는 궁금증이 생겼습니다. 네, 이 글을 적고 있으니 당연히 방법을 찾았겠지요?


자바스크립트에서 ES6 이상에서만 템플릿 문자열을 쓸 수 있는 것처럼 파이썬 역시 Python 3.6 이후 버전에서 비슷한 역할을 하는 f-Strings 가 추가되었습니다. f-Strings 라고 이름 붙인 연유는 찾아보지 않았지만 문자열을 묶어주는 따옴표 앞에 f 를 붙여야만 이 기능을 쓸 수 있기 때문에 그냥 그렇게 이름을 지었구나... 참 엔지니어스럽다 라는 생각을 하고 넘어갔습니다. 

# Print 문에서 f-Strings 사용하기
print(f"- Start update : {_domain}/{_property}")

# 문자열에서 f-Strings 사용하기
log += f"- Start update : {_domain}/{_property}\n"

# f-Strings 로 LIST 바인딩하기
log += f"- No metadata found ({result['title']})\n"

 대표적으로 f-Strings 를 사용한 예시들입니다. 문자열이 필요한 모든 곳에 사용할 수 있습니다. 자바스크립트 ES6 에서는 ${} 였다면 파이썬 에서는 {} 입니다. 브레이스 안쪽에는 직접 변수명이 들어가거나 내부 함수, 메서드 등이 모두 활용될 수 있습니다. 이런 형태의 문자열 조립이 좋은 이유는 정확하게 출력되는 결과를 인지할 수 있다는 점이겠지요? 아래와 같은 형태에서는 가독성도 떨어지고 무슨일이 일어날지 짐작하기가 어려우니까요.

print("- Start update : " + _domain + "/" + {_property})
log += "- No metadata found (" + result['title'] + ")\n"

[ 윤작가님의 베스트셀러 파이썬 서적으로 고고씽! ]

 

혼자 공부하는 파이썬:파이썬 최신 버전 반영

COUPANG

www.coupang.com

 

 

python, JSON 타입과 DICT 타입의 자료형 변환

JSON 으로 데이터를 표현하는 것은 굉장히 일반화 되어 있습니다. 많은 API 의 응답이 JSON 형태를 가지고 있을 뿐만 아니라 서버의 설정이나 개발된 코드의 환경 정보도 JSON 으로 쓰는 경우가 많습�

ondemand.tistory.com

 

 

Python 에는 증감연산자가 있다, 없다?

전통적인(?) 많은 개발 언어에서 <증감연산자>를 사용해 왔습니다. ++ 혹은 -- 로 표기되는 <증감연산자>는 <3항 연산자>와 마찬가지로 코드의 양을 많이 줄여줄 수 있는 장점이 있어 널리 사용됩니

ondemand.tistory.com

 

728x90
728x90

JSON 으로 데이터를 표현하는 것은 굉장히 일반화 되어 있습니다. 많은 API 의 응답이 JSON 형태를 가지고 있을 뿐만 아니라 서버의 설정이나 개발된 코드의 환경 정보도 JSON 으로 쓰는 경우가 많습니다. 하지만 JSON 이 정말 편안한 형태인가? 에 대해서는 쉽게 고개를 끄덕이긴 어렵습니다. 파싱(Parsing) 하는 과정이 은근 까다로울때가 많고, 특히 JSON 이 중첩되어 있을 경우에는 더욱 그렇습니다. 

파이썬에서 requests 모듈을 이용하여 JSON 으로 응답하는 API 를 호출하기 위하여 아래와 같이 간단한 코드를 만들었습니다. 응답 결과가 JSON 이기 때문에 응답에 대하여 JSON 으로 쉽게 읽을 수 있겠지? 하는 마음에 json() 메서드를 호출해서 변수에 담았습니다. 

import requests
import json

_target = "https://www.example.com/api"
s = requests.Session()
result = s.get(_target).json()

데이터는 JSON 의 모습이지만 이 자료가 어떤 형태인지는 type 으로 찍어보거나 requests 모듈의 문서를 찾아서 json() 메서드의 자료형을 살펴봐야 합니다. 간단히 코드를 만들어서 확인해보니 문자열 형태가 아닌 Dictionary 형태의 자료였습니다. print 문 등으로 출력을 해보면 마치 JSON 처럼 생긴 결과가 나오지만 JSON 의 규격과 맞지 않아 JSON Validator 를 이용해보면 파싱이 제대로 되지 않습니다.

여기서 또 한가지의 문제는 이 응답 결과를 for 문으로 이용하는 코드를 만들었을 때 생깁니다. Dictionary 형태의 데이터였지만 특정 Key 의 값을 꺼내어 확인해보면 Dictionary 가 아닌 List 형으로 변경된 것을 알 수 있습니다. 자료형이 어떻게 바뀌어 가는지를 제대로 가늠하지 못하고 코드를 만들다 보니 한참을 헤메일 수 밖에 없었습니다. 결과에서 특정한 값이 필요할 때는 Dictionary 가 편하지만 Javascript 등에서 JSON 을 필요로 할 때는 문자열 형태로 전달해서 JSON 으로 파싱할 수 있도록 해주는 것이 좋습니다. 

result = s.get(_target).json()
print(type(result))             # DICT
result_json = json.dumps(result)
print(type(result_json))        # JSON
result_dict_again = json.loads(result_json)
print(type(result_dict_again))  # DICT

json 모듈이 제공하는 json.dumps() 메서드는 자료형을 JSON 형태로 (단순 문자열입니다!) 바꿔주는 역할을 하고, json.loads() 메서드는 문자열 자료를 파싱하여 Dictionary 의 Key, Value 형태로 변경해줍니다. 파이썬 내에서 자료를 핸들링 할때는 Dictionary 를 이용하고 외부로 전달할 때는 JSON 형태로 쉽게 바꿀 수 있게 되었습니다. 오늘도 파이썬 초보는 이렇게 한가지를 또 배워갑니다. 


사놓고 보지 않는 혼공파... 틈틈히 읽어야겠습니다 ㅜㅜ (반성..반성..)

 

 

혼자 공부하는 파이썬:파이썬 최신 버전 반영

COUPANG

www.coupang.com

 

Python 에서 선언되지 않은 변수를 처리하는 방법

언어마다 선언되지 않은 변수를 처리하는 방법들이 조금씩 다른 것 같습니다. 가령 Javascript 에서는 변수가 선언되지 않았을 때 undefined 인지를 확인하는 방법으로 선언되지 않은 변수를 조건문에서 확인하는..

ondemand.tistory.com

 

Python 프로젝트의 Package 를 쉽게 추출하고 설치하기

Python 을 조금씩 좋은 감정으로 알아가고 있는 중입니다. 결실을 맺게 될지는 잘 모르겠지만 오늘도 새롭게 공부한 것들이 있어서 공유해 봅니다. Python 에서 Package 관리를 위해 사용하는 것이 pip 혹은 pip3..

ondemand.tistory.com

본 포스팅은 소정의 수수료를 지급받을 수 있습니다

728x90
728x90

서버에 ssh 로 접근하기 위한 인증서 파일이라던가 크리덴셜 정보는 닷(.)으로 시작하는 숨김 폴더에 저장하는 경우가 많습니다. 터미널에서는 기본적으로 이런 숨김 폴더를 쉽게 볼 수 있기 때문에 문제가 없습니다. 하지만, finder 를 이용해서 파일의 위치를 옮기거나 하는 작업이 필요한 경우 숨김 폴더가 보이지 않아 당황하는 경우가 간혹 생기죠. 

 

 

macOS 버전에 따라 방법이 조금씩 다르지만 최근의 버전을 사용하고 있다면 finder 창에서 간단히 <shift+cmd+.> 의 키 조합을 눌러주는 것만으로 숨김파일/폴더를 볼 수 있습니다. 할 때마다 까먹고, 또 누군가는 저처럼 어떻게 하는지를 찾으실 것 같아서 기록으로 남겨봅니다. 할때마다 새로운 기분이고 재미있다는 JS Park 님의 인터뷰가 떠오르는 아침입니다. 

 

 

혹시 위의 방법이 적용되지 않는 오래된 버전의 Mac 을 사용하고 있다면 아래의 포스팅을 참고해 보시기 바랍니다. 예전에는 정말 번거로운 방법으로 숨김파일이나 폴더를 볼 수 있었으니... macOS 는 느리지만 계속 진화하고 있습니다 ^^;;

 

 

맥(Mac) 파인더(Finder)에서 숨김파일 표시 활성화하는 방법

회사에서 지급받은 맥북에어(MacBook Air)가 3년을 넘기면서 슬슬 배터리 광탈 현상을 보이기 시작했습니다. 아직 회사에서 터치바(Touch Bar)가 내장된 신형 맥북을 지급하고 있지 않아 노트북 교체 신청을 할까..

ondemand.tistory.com

 

728x90
728x90

 

언어마다 선언되지 않은 변수를 처리하는 방법들이 조금씩 다른 것 같습니다.

가령 Javascript 에서는 변수가 선언되지 않았을 때 undefined 인지를 확인하는 방법으로

선언되지 않은 변수를 조건문에서 확인하는 방법을 많이 사용합니다.

(물론, 이는 Javascript 의 구조적인 특성에서 기인한 부분이라 좀 특별하긴 하죠)

오래전의 기억을 더듬어 보면 C# 의 경우 null 등으로 비교할 수 있었던 것 같습니다. 

 

Python 에서 코드를 만들다 선언되지 않은 변수를 처리하려다 보니

익숙한 다른 언어들하고는 조금 다르게 접근을 해야하는 것 같더군요.

물론 문제를 해결하기 위해 몇 가지 검색을 해본 것이라 다른 더 좋은 방법이 있을수도 있습니다. 

 

# try~except 를 이용하는 방법
try:
	target_list
except NameError:
	target_list = None
    
if target_list = None:
	# do something if it's not defined
else:
	# do something with variable
	

 

첫번째로 찾은 방법은 Exception 을 이용하는 것이었습니다.

Python 에서는 명시적으로 선언되지 않은 변수를 찾는 Method 가 없다고 합니다.

try~except 문을 이용하여 변수가 선언되지 않았을 때, 특별한 예약 값 None 을 넣고 if 문을 활용하는 방법입니다.

훌륭해 보이긴 하는데 왠지 코드의 가독성이 좋은 느낌은 아닙니다.

try 구문에 # do something if it's not defined 에 구현할 내용을 넣으면 조금 나아질 수는 있겠죠?

 

# locals(), globals() 를 뒤져보기
if 'target_list' not in locals():
	# do something if target_list is not exist
else :
	# do something with target_list

 

다른 방법은 Python 의 변수 특성을 이용하는 방법입니다.

Python 에서 변수가 선언되면 지역 변수냐 글로벌 변수냐에 따라

locals() 와 globals() 함수가 리턴하는 결과를 이용하여

이미 선언된 변수인지 아닌지를 확인해 볼 수 있습니다.

 


더 좋은 방법이 있다면 파이썬 알못인 저에게 도움 부탁드리겠습니다!
어찌저찌 돌아가기만 하는 코드는 왠지 찝찝하니까요!

728x90
728x90

pip 가 참 많은 일을...

 

Python 을 조금씩 좋은 감정으로 알아가고 있는 중입니다. 결실을 맺게 될지는 잘 모르겠지만 오늘도 새롭게 공부한 것들이 있어서 공유해 봅니다. Python 에서 Package 관리를 위해 사용하는 것이 pip 혹은 pip3 입니다. npm 과 같은 Package 관리자라고 생각하니 마음이 편해졌습니다.

 

npm 에서는 프로젝트 루트 경로의 package.json 에 패키지를 기술해두고 npm install 명령으로 패키지를 쉽게 설치할 수 있었는데요, pip 에는 그런 기능이 없나 하고 살펴보니 역시나 제공하고 있었습니다. 당연한 것을 이렇게 하나씩 알아가는 것도 새로운 개발 환경을 익히고 배우는 재미이겠지요.

 

// 패키지의 목록을 출력
$ pip freeze
boto3==1.12.24
botocore==1.15.24

// 패키지의 목록을 requirements.txt 에 저장
$ pip freeze > requirements.txt

 

먼저 pip 의 freeze 를 이용하여 사용중인 패키지를 확인할 수 있습니다. 가상 환경을 적절히 분리해 두지 않았다면 전역으로 설치된 모든 패키지의 목록이 나오는 것처럼 보입니다. 아직 Python 가상환경에 익숙치 않아서... 대충 그렇게 돌아가는 것처럼 보이는 느낌입니다 ㅎㅎ.

 

$ pip install -r requirements.txt

 

requirements.txt 파일은 `패키지명=버전` 혹은 `패키지명` 으로 패키지에 대하여 기술한 목록 파일입니다. NodeJS 에서는 package.json 에 JSON 형태로 모듈명과 버전명을 기술했던 것에 비해 단순합니다. pip install 명령을 -r #패키지목록# 형태로 실행하면 지정되어 있는 패키지의 버전을 찾아 설치하게 됩니다. 참 쉽죠? 

 

 

728x90
728x90

 

델리미터(Delimeter)로 컬럼이 분기된 텍스트 파일(보통은 아마도 로그파일)을 다룰 때 가장 손쉽게 쓸 수 있는 도구가 awk 입니다. 물론 cut 등도 있지만 탐색한 이후의 작업을 주로 담당하기 때문에 탐색시에는 역시 awk 가 대세입니다. (당연하겠습니다만 awk 도 정규표현식의 탐색을 지원하니 자세한 내용은 글 맨 아래의 포스팅 링크를 참고해 주세요.)

 

 

awk 에서 특정 컬럼의 값을 표현하는 방법은 여러가지가 있습니다. 그동안 많이 사용했던 패턴은 아래와 같은 방식이었는데요 이 방식에서도 or, and 조건을 사용하는 것은 문제가 없습니다. 조건을 동시에 만족하는 경우를 찾으려면 &&를, 어느 하나라도 만족하는 경우를 찾으려면 || 를 사용하면 됩니다. 

 

// AND (&&)
awk '$2=="" && $19~/my/ {print $11, $24}'

// OR (||)
awk '$2=="" || $19~/my/ {print $11, $24}'

 

이렇게 표현하는 방법 외에 브레이스({}) 안쪽에 if 문을 넣어서 사용하는 방법도 있습니다. 어느 방법을 사용하던 원하는 결과를 얻을 수 있으면 되니 손에 더 감기는 방법을 사용하면 좋을 것 같습니다. NOT 조건을 사용하는 경우에도 보다 쉽고 명시적으로 읽을 수 있어서 이 방법을 개인적으로는 선호하는 편입니다. 

 

// AND (&&)
awk '{ if ($5=="/test" && $3=="GET") print $0}' 

// OR (&&)
awk '{ if ($5=="/test" || $5=="/beta") print $0}' 

// NOT (!)
awk '{ if (!($5=="/test")) print $0}' 

 


 

쉘 스크립트 - awk 에서 정규 표현식 사용하기

쉘에서 로그와 같은 텍스트 파일을 다룰때 정규표현식을 자주 사용하게 됩니다. 정규표현식을 지원하는 쉘의 도구들은 여러가지가 있는데요 오늘은 awk 에서 정규표현식을 사용하는 방법을 간단하게 살펴보겠습니..

ondemand.tistory.com

 

728x90
728x90

네, 없습니다.. ㅡ,.ㅡ;

 

전통적인(?) 많은 개발 언어에서 <증감연산자>를 사용해 왔습니다.

++ 혹은 -- 로 표기되는 <증감연산자>는 <3항 연산자>와 마찬가지로

코드의 양을 많이 줄여줄 수 있는 장점이 있어 널리 사용됩니다.

 

가령 아래의 코드와 같은 방법으로 반복문에서 많이 사용되고 있고

반복적인 작업을 처리하는 로직 내에서도 ++ 혹은 -- 로 

카운트의 값을 변경하는 목적으로 널리 애용되고 있습니다. 

 

for (int i=0; i<100; i++) {}

 

그런데!

 

Python 에서는 <증감연산자>가 사용되지 않고 존재하지 않는다는 사실.

오랫동안 Python 을 다뤄오신 분들에게는 "당연한거 아니야?" 라는 반응이 나오겠습니다만

어쩌다 한 번 Python 으로 코드를 만들어야 하는 일이 생기는 분들이라면

존재하지 않는 <증감연산자>에 당황할 수 있습니다... 네, 제가 그랬으니 이 글을 쓰고 있...

Python 에는 아래와 같은 방식으로 for 문을 대체하는 것이 일반적이더군요.

 

for i in range (0,99):
	# blah blah...

 

 

반복문이 아닌 경우에는 어떻게 하면 될까요?

갑자기 쓰려니 공식 명칭이 떠오르지 않습니다만 += 나 -= 는

다른 언어에서와 마찬가지로 Python 에서도 사용되는 연산자입니다. 

 

# 다른 언어
i++;
i--;
++i;
--i;

# Python
i += 1
i -= 1

 

Python 은 누구나 쉽게 읽을 수 있고 명료한 코드를 추구한다고 합니다.

<증감연산자>는 분명 간편하지만 전위인지, 후위인지에 따라

실제 값이 어느 타이밍에 바뀌느냐로 굉장히 골머리를 썩었던 경험들이 다들 있을겁니다.

C 언어에서의 포인터 급은 아니지만 처음 개발에 입문할 때 꽤 많은 오류를 만들었던 기억이 납니다.

혹시나 생길 수 있는 코드 해석의 차이를 없애주는 착한 Python 입니다. 

 


 

혼자 공부하는 파이썬:파이썬 최신 버전 반영

COUPANG

www.coupang.com

 

Visual Studio Code 에서 Python 패키지의 Unresolved Import Error 를 처리하는 방법

코드를 만들 필요가 있을때 왠만하면 Node.js 를 이용하는 편입니다. 아무래도 익숙하기도 하거니와 자유로운 자바스크립트의 DNA 가 살아 있기 때문에 "대략 이렇게 돌아갈까?" 하는 것들이 동작하기 때문입니다...

ondemand.tistory.com

 

맥에서 파이썬 개발환경 준비하기

파이썬은 2.x 대와 3.x 대가 있는데, 서로 호환성이 없음 일반적으로 많이 사용되는 2.x 대를 설치 - 설치안내 : http://python.org/download/releases/2.7.6/ 파이썬 패키지 관리도구 : easy_install, pip pip 는..

ondemand.tistory.com

 

본 포스팅은 소정의 수수료를 받을 수 있습니다

 

728x90

+ Recent posts