IT/컴퓨터 (26개의 글)

프로젝트를 처음으로 github 저장소에 올리는 법 

2020. 06. 23 IT/컴퓨터 > 기타 프로그래밍
개발자라면 누구나 쓰게 되는 버전 관리 시스템 git. 왜 이렇게 익숙해지지 않는지 모르겠다. Git 관련된 명령어도 좀 쭉 정리를 해보고 싶은데, 매일 구글링에 허덕이며 겨우겨우 다른 프로젝트 개발을 하는 입장에서 다른 사람한테 쉽게 설명할만큼 이해가 깊지도 않고 시간도 없어서 여의치가 않다. 이게 여러 사람이랑 프로젝트를 하면 브랜치도 만들었다가 합쳤다가 다양하게 사용법을 익힐 수 있을지 모르겠는데, 방구석 취미 프로그래머로서는 뭐 대단한 기능을 만드는것도 없고 귀찮아서 한 브랜치에서 쭉 개발을 하게 되니 매일 git push, git pull 만 하게 된다. 그러다 보니 아주 가끔 하게 되는 일들이 기억이 안나서 매번 검색하기가 불편하다. 일단 오늘은 새로 저장소를 만들고 처음 프로젝트 세팅을 하는 방법을 정리해본다.   가장 기본인데 프로젝트 처음에 딱 한번 하고 그후로는 할 일이 없으니 매번 잊어버린다.   #1. 우선 commit 하는 사용자 정보를 설정해준다. 이제 commit 할때마다 위의 정보로 commit 이 되게 된다.   #2. 프로젝트의 root 디렉토리로 이동해서 초기화를 해준다. 초기화가 정상적으로 되면 branch 이름이 경로에 보이게 되는데, 처음에는 (master) 라고 나온다. 나중에 브랜치를 만들고 바꾸게 되면 괄호 안의 내용이 바뀔때도 있다.   #3. 미리 .gitignore 파일을 작성해서 업로드 시에 제외할 파일과 폴더를 설정 .gitignore (이름이 없고 확장자가 gitignore 이다) 파일은 프로젝트의 root 폴더에 작성해서 저장해주면 된다. 이 안에는 데이터베이스 파일이나 임시로 생성되는 캐시 파일, 기타 프로젝트에 필요 없지만 자동으로 생성되는 파일 등을 적어 놓으면 git 이 알아서 해당 파일들을 추적에서 제외해준다. * 그런데 이미 한번 push를 해 버린 파일이나 디렉토리는 단순히 .gitignore에 추가로 적어준다고 해서 지워지지 않는다. 이 때는 추가 작업이 필요하다 (다음 포스트에 정리)   #4. git 에서 추적할 파일을 전부 staging 해준다. 추가할 파일 이름을 지정해줄 수도 있지만 최초 프로젝트 업로드시에는 모든 파일을 올릴 것이므로 add . 을 해 주면 된다. add 한칸 띄고 마침표.   #5. 최초의 커밋을 작성 git commit 에 -m 옵션을 달면 커밋 설명을 한번에 같이 저장할수 있다.  보통 최초의 커밋은 first commit 또는 initial commit 등으로 지정하면 된다.   #6. github remote 저장소 (repository) 를 지정 github.com 에 계정을 만들고 저장소를 미리 만들어야 한다. 저장소 만드는 방법은 직관적이므로 생략. https:// 다음에 나오는 원격 저장소를 origin 이라는 이름으로 업로드할 위치로 지정해 주겠다는 의미이다. 꼭 origin 으로 하지 않아도 되는것 같지만 업계의 관행이므로 특별한 이유가 없다면 바꾸지 않아도 될듯.   #7. 지정된 저장소에 push git push 는 로컬의 코드를 원격 저장소에 업로드하는 명령이다. 최초에는 -u 플래그를 붙여서 올려줘야 한다고 한다. 이 -u 를 설정해줘야 git pull 명령을 할 때 따로 어느 브랜치에서 pull 해 올지 설정을 해 주지 않아도 된다고 하는데.. 최초에 -u를 붙여주면 그 후에는 커밋 후 git push 만 해 줘도 잘 올라간다.   #8. 서버에서 다운로드받기  처음 프로젝트를 다운받을때는 git clone 을 하면 원격 저장소에 있는 모든 파일을 다운받고 브랜치 및 commit 정보를 다 가져온다. 이후에는 git pull 을 통해 변경사항만 반영하는 것이 가능해진다.   다음 포스트에 몇가지 삽질 해결용 명령어도 정리해보겠다. Git 덮어쓰기, .gitignore 새로 반영하기 등

파이썬 문자열(string) 다루기 - 메서드 정리 

2020. 04. 23 IT/컴퓨터 > Python
  파이썬을 다루다 보면 가장 많이 하게 되는 작업 중 하나가 문자열을 다듬는 작업일 것이다. 문자열 관련된 메서드들을 정리해 본다. 파이썬의 문자열은 작은따옴표(' ') 또는 큰따옴표(" ") 사이에 원하는 문자열을 입력해서 생성할수 있고, 여러 줄로 된 문자열을 한번에 생성하고 싶은 경우 작은따옴표나 큰따옴표 3개를 시작과 끝 부분에 입력해서 생성할 수 있다.   파이썬의 문자열은 불변(immutable) 이므로 메서드가 원래의 문자열을 변경하는 경우는 없다. 메서드는 항상 새로운 문자열을 반환하므로 이 새로운 문자열을 저장해 줄 새로운 변수를 마련해 주어야 한다. 예전에 어떤 함수 안에서 text = text.replace('/', '') 식으로 새로 변경된 문자열을 다시 변수에 할당해 주어야 하는데 무의식중에 원래의 텍스트를 변경하는 것으로 생각했는지 text.replace('/', '') 라고만 적어놓고선 프로그램이 작동하지 않아서 한참 고생한 적이 있다.   str.capitalize() 문자열의 첫 글자만 대문자로 만들고 나머지는 소문자로 된 문자열을 반환한다. 이때 첫 문자가 영문 알파벳이 아닌 경우는 변환되지 않은 원래 문자 그대로이며 나머지 문자들만 소문자로 바뀐다.   str.lower() 원래 문자열에서 영문 알파벳 문자를 모두 소문자로 바꾼 문자열을 반환한다. 영문 알파벳이 아닌 문자는 무시한다.   str.upper() 원래 문자열에서 영문 알파벳 문자를 모두 대문자로 바꾼 문자열을 반환한다. 영문 알파벳이 아닌 문자는 무시한다.   str.title() 문자열을 타이틀 규칙에 맞게 바꾼 새 문자열을 반환한다. 영문 알파벳이 아닌 글자 다음에 나오는 첫 알파벳 문자는 대문자로, 알파벳 문자 뒤에 나오는 알파벳 문자는 소문자로 바꾸는 것이다. 보통은 공백 문자 다음에 나오는 알파벳을 바꾸는 목적이지만, 공백문자가 아닌 어포스트로피(') 등의 문자 뒤에 따라오는 문자도 대문자로 바꾸어 버리기 때문에 그다지 쓸모있는 메서드는 아닌것 같다. 공식 문서에서는 정규표현식을 사용해 딱 빈칸 뒤에 오는 알파벳만 대문자로 바꾸는 방법이 있다고 소개하고 있지만 그렇게 하면 배보다 배꼽이 더 커지는것같다..   str.casefold() 전부 소문자로 된 문자열을 반환한다. str.lower()와 비슷하지만 더욱 엄격하게 적용된다고 하는데, 영어가 아닌 다른 언어에서 차이점을 나타내는 경우가 있는 듯하다. 예를 들어 독일어 ß 의 경우 lower() 로는 그대로 ß 이지만 casefold() 를 적용하는 경우 'ss' 로 바뀐다. 영어 이외의 라틴어권 문자를 다룰 경우 쓸 일이 있을지도 모르겠다.   str.swapcase() 영문 대문자는 소문자로, 소문자는 대문자로 바뀐 문자열을 반환한다. 영문 알파벳이 아닌 문자는 변하지 않는다.     str.center(width[, fillchar]) width 에 지정해준 길이만큼의 빈 문자열을 만들고 원래의 문자열을 이 가운데 위치시킨다. 옵션으로 fillchar 를 지정해 주면 빈 문자 대신 fillchar 로 공간을 채운다. width 가 원래 문자열의 길이보다 작게 지정되었다면 원래의 문자열을 그대로 반환한다.   str.ljust(width[, fillchar]) 원래의 문자열을 왼쪽 정렬하고 width 로 주어진 길이만큼 공백 문자로 채운 문자열을 반환한다. fillchar를 따로 지정해주면 공백 대신 그 문자로 공간을 채운다. width 가 원래 문자열의 길이보다 작은 숫자이면 원래 문자열을 반환한다.   str.rjust(width[, fillchar]) 원래의 문자열을 오른쪽 정렬하고 width 로 주어진 길이만큼 공백 문자로 채운 문자열을 반환한다. fillchar를 따로 지정해주면 공백 대신 그 문자로 공간을 채운다. width 가 원래 문자열의 길이보다 작은 숫자이면 원래 문자열을 반환한다.     str.count(sub[, start[, end]]) 특정 문자열이 원래 문자열 안에서 몇 번이나 등장하는지를 알려 준다. 옵션으로 start와 end를 지정할 수 있는데, 일반적인 문자열 슬라이스 할때와 같이 인덱스를 지정해주면 된다. start와 end를 지정해주면 그 범위 안에서만 검색한다.     str.startswith(prefix[, start[, end]]) 문자열이 prefix로 주어진 문자열로 시작되는지 검사해서 True 또는 False 를 반환해준다. Prefix 는 하나의 문자열일수도 있고, 튜플로 여러 문자열을 검사하도록 할 수도 있다. 튜플로 검사하는 경우 원소 중 하나만 일치하면 True가 반환된다. 옵션으로 start, end 인덱스를 주면 start 인덱스에서 검사를 시작하고 end 인덱스까지만 검사한다.   str.endswith(suffix[, start[, end]]) 원래 문자열이 suffix 로 주어진 문자열로 끝나는지를 검사해서 True 또는 False 를 반환한다. Suffix 는 하나의 문자열일수도 있고, 튜플로 여러 문자열을 검사하도록 할 수도 있다. 튜플로 검사하는 경우 원소 중 하나만 일치하면 True가 반환된다. 옵션으로 start, end 인덱스를 주면 start 인덱스에서 검사를 시작하고 end 인덱스까지만 검사한다 (오른쪽부터).     str.find(sub[, start[, end]]) 원래 문자열에서 sub 으로 주어진 문자열을 찾는다. sub 문자열이 있는 경우 그 문자열이 시작되는 지점의 index를 반환해준다. sub 문자열을 찾지 못한 경우 -1 이 반환된다. 만약 문자열이 시작되는 위치를 찾고 싶은것이 아니라 그냥 문자열이 포함되어 있는지 여부만 알고 싶다면 find() 대신 'in' 연산자를 사용하는 것이 더 간단할 수도 있다.   str.index(sub[, start[, end]]) find() 와 같은 기능이지만, sub 문자열이 없는 경우 ValueError 를 일으킨다.   str.rfind(sub[, start[, end]]) 왼쪽에서부터 검색하는 find() 와 반대로, 원래 문자열에서 sub 으로 주어진 문자열을 오른쪽에서부터 찾아 인덱스를 반환한다 (즉 가장 마지막에 sub 문자열이 나오는 곳의 인덱스). sub 문자열을 찾지 못한 경우 -1 이 반환된다.   str.rindex(sub[, start[, end]]) rfind() 와 같은 기능이지만, sub 문자열이 없는 경우 ValueError 를 일으킨다.     str.isalnum() 문자열이 알파벳과 숫자로만 이루어져있으면 True, 아니면 False 를 반환한다. 구체적으로는 문자열의 각 글자가 isalpha(), isdecimal(), isdigit(), isnumeric() 중 하나에 해당하면 해당 글자는 isalnum() 에 대해서 True 이다. 한글로 해 보면 한글도 True를 반환한다.   str.isalpha() 문자열이 알파벳으로만 이루어져 있으면 True, 한 글자라도 다른 글자가 들어 있으면 False를 반환한다.   str.isascii() 파이썬 3.7 에서 새로 생긴 메서드. 문자열의 모든 문자가 ASCII 문자이면 True, 아니면 False를 반환한다. 공백 문자도 True를 반환한다.   str.isdecimal() 문자열의 모든 문자가 10진수 숫자이면 True, 아니면 False를 반환한다.   str.isdigit() 문자열의 모든 문자가 숫자로만 이루어져 있으면 True, 아니면 False를 반환한다. 10진수를 구성하는 0~9까지의 숫자 이외에도 다른 언어 체계에서 숫자를 나타내는 특별한 문자들도 True를 반환한다고 하는데, 공식 홈페이지에서는 Kharosthi 문자라는걸 예로 들고 있어서 찾아 보았더니 고대 인도(?) 문자라고 한다..이런걸 쓸 일이 있을지.. 아마 일상적인 용도에서는 isdecimal()과 똑같은 용도로 쓰면 될 듯.   str.isnumeric() 문자열의 모든 문자가 numeric 문자이면 True, 아니면 False를 반환한다. isdigit() 에 포함된 문자 이외에도 Unicode에서 numeric 값을 가진 문자들이 모두 포함된다고 한다. 10진수를 다루는 일상적인 용도에서는 역시 isdecimal() 과 차이 없을듯.   str.islower() 문자열의 영문 문자가 모두 소문자이면 True, 아니면 False를 반환한다. 알파벳이 아닌 문자는 무시한다. 알파벳 문자가 하나도 포함되어 있지 않으면 False를 반환한다.   str.isspace() 문자열의 모든 문자가 공백 문자 ' ' 이면 True, 아니면 False를 반환.   str.istitle() 문자열이 title 형식에 맞으면 True, 아니면 False를 반환. 구체적으로는 영문 대문자 앞에는 대문자/소문자가 아닌 문자만 와야 하고 (대문자 앞에 공백문자가 오는 것이 일반적이지만, 한글 문자 등 알파벳이 아닌 문자가 있는 경우에도 True를 반환한다.), 영문 소문자 앞에는 꼭 영문 소문자 또는 대문자가 있어야 한다.   str.isupper() 문자열의 영문 문자가 모두 대문자이면 True, 아니면 False를 반환한다. 알파벳이 아닌 문자는 무시한다. 알파벳 문자가 하나도 포함되어 있지 않으면 False를 반환한다.     str.join(iterable) 반복 가능한 객체(리스트, 튜플 등)을 인자로 주면 객체의 각 원소를 주어진 string 을 이용해 연결해서 새로운 문자열을 반환해 준다. 반복 객체의 원소가 문자열 타입이 아닌 경우에는 TypeError 가 발생한다.   str.split(sep=None, maxsplit=-1) 주어진 문자열을 sep 문자열이 나오는 곳마다 잘라서 잘린 문자열들로 이루어진 리스트를 반환한다. sep을 따로 지정하지 않으면 공백 문자가 나오는 곳마다 자른다. 이 때 공백문자가 연속해서 나오는 것은 무시하고 공백문자를 제거하고 잘려서 남은 문자열들만 리스트의 원소로 집어넣는다. sep을 지정해주면 이 문자열을 기준으로 자르는데, 주어진 문자가 연속해서 나오는 경우에 연속된 sep 문자 사이사이에 빈 문자열이 있는것으로 간주해 빈 문자열 (공백 문자가 아니라 empty string) 를 리스트의 원소로 집어넣는다. (즉 default 인 공백 문자를 기준으로 split할때와 동작이 다름) maxsplit 을 지정해주면 왼쪽부터 정해진 횟수만큼만 자르는 작업을 해서 리스트로 반환한다.   str.rsplit(sep=None, maxsplit=-1) 주어진 문자열을 sep 문자열이 나오는 곳마다 잘라서 잘린 문자열들로 이루어진 리스트를 반환한다. sep을 따로 지정하지 않으면 공백 문자가 나오는 곳마다 자른다. maxsplit 을 지정해주면 오른쪽부터 정해진 횟수만큼만 자르는 작업을 해서 리스트로 반환한다. 오른쪽부터 자르는 점만 제외하면, 왼쪽부터 잘라 나가는 split() 와 동작은 동일하다.   str.splitlines([keepends]) 문자열 중에 개행 문자를 찾아 이를 기준으로 문자열을 잘라서 리스트로 반환해준다. 디폴트는 개행 문자는 제거한 리스트를 반환해 주지만, keepends=True 를 주면 개행문자가 포함된 원소들로 이루어진 리스트를 반환해준다. 자르는 기준이 되는 개행 문자들은 다음과 같다. \n : Line Feed \r : Carriage Return \r\n : Carriage Return + Line Feed \v or \x0b : Line Tabulation \f or \x0c : Form Feed \x1c : File Separator \x1d : Group Separator \x1e : Record Separator \x85 : Next Line (C1 Control Code) \u2028 : Line Separator \u2029 : Paragraph Separator     str.strip([chars]) 문자열의 좌우에 있는 공백 문자들을 제거한 문자열을 반환해준다. 따로 chars 문자열을 지정해주면 해당 문자열의 문자들로 조합할 수 있는 모든 문자열에 대해 왼쪽과 오른쪽에서 제거를 시도한다.   str.lstrip([chars]) 원래 문자열에서 왼쪽에 있는 공백 문자를 전부 제거한 문자열을 반환한다. 문자열을 인수로 전달하면 공백문자 대신 해당 문자열을 제거한다. 이 때 정확히 해당 문자열을 제거하는것이 아니고, 주어진 문자열을 조합해서 나올 수 있는 모든 문자열에 대해 제거를 시도한다. (메서드를 사용하기 위해서 문자의 조합이 어떤 것이 나올수 있는지 계산해 봐야 하는것인가..) 유용하게 쓰는 경우는 웹에서 텍스트를 크롤링했을때 원하는 텍스트 앞뒤로 빈칸이 있는 경우가 많은데 이때 lstrip(), rstrip(), strip() 등을 이용해 빈칸이 제거된 깔끔한 문자열을 얻을수 있다.   str.rstrip([chars]) 원래 문자열에서 오른쪽에 있는 공백 문자를 전부 제거한 문자열을 반환한다. 문자열을 인수로 전달하면 공백문자 대신 해당 문자열을 제거한다. 이 때 정확히 해당 문자열을 제거하는것이 아니고, 주어진 문자열을 조합해서 나올 수 있는 모든 문자열에 대해 제거를 시도한다. 오른쪽에서 제거하는 점을 빼면 lstrip() 과 같다.     str.partition(sep) sep 으로 주어진 문자열이 처음 나오는 곳에서 문자열을 3개로 잘라 튜플로 반환해 준다(sep 의 앞부분, sep, sep의 뒷부분). 원래 문자열에 sep 문자열이 없는 경우에는 원래 문자열과 빈 문자열 2개로 된 튜플을 반환한다.   str.rpartition(sep) sep 으로 주어진 문자열이 처음 나오는 곳에서 문자열을 3개로 잘라 튜플로 반환해 준다(sep 의 앞부분, sep, sep의 뒷부분). 원래 문자열에 sep 문자열이 없는 경우에는 빈 문자열 2개와 원래 문자열로 된 튜플을 반환한다.     str.replace(old, new[, count]) 문자열 안에서 특정 문자열을 전부 찾아 새로운 문자열로 바꿔 준다. 개인적으로 프로젝트 관련해서 디렉토리 내의 파일 이름을 대량으로 바꾸는 작업을 할 때가 가끔 있는데 이때 유용하게 쓰는 메서드이다. 옵션으로 count 를 주면 count 에 주어진 횟수만큼만 바꾸고 작업을 멈춘다.   str.zfill(width) 원래의 문자열 왼쪽에 숫자 '0' 을 채워 width 만큼의 길이를 가진 문자열을 만들어 반환한다. width 가 원래 문자열 길이보다 작으면 원래 문자열을 그대로 반환한다. 특이점은 문자열이 + 또는 - 기호로 시작하는 경우에는 숫자 0 을 맨 왼쪽부터 채우는 것이 아니고 + 또는 - 기호가 가장 먼저 나오고 그 다음 0을 채우고, 그 후에 원래 문자열의 나머지 부분이 나온다.   이상으로 파이썬 문자열 관련된 메서드들을 정리해 보았다. 이외에도 몇가지 메서드들이 더 있지만 유니코드 등 관련된 전문적인 작업을 하는 경우가 아니면 별로 쓸 일이 없을것같아 생략했다. 문자열 포매팅 관련된 format() 메서드는 내용이 많아 나중에 따로 정리해 보려고 한다.

파이썬 datetime 모듈로 날짜, 시간 다루기 

2020. 04. 03 IT/컴퓨터 > Python
파이썬으로 코딩을 하다 보면 날짜, 시간에 관련 처리를 할 일이 많다. 이때 사용하게 되는것이 datetime 모듈이다. 시간을 처리하는 다른 모듈은 time 모듈이 있는데, time 모듈은 좀 더 근본적으로 시간에 관한 여러 가지 연산을 지원하고, datetime 은 기본적인 연산들은 지원하지만 주로 시간이나 날짜를 사용자에게 보여지는 형태로 포매팅하는데 중점을 둔 모듈이라고 한다.   Datetime 모듈의 하위 클래스는 datetime.date datetime.time datetime.datetime datetime.timedelta datetime.tzinfo datetime.timezone 이 있다. 시간대 적용은 pytz 모듈로 하는것이 편하므로 이 중 tzinfo 와 timezone 은 생략하고 나머지 모듈에 대해서 정리해 본다. 더 자세한 내용은 공식 문서 참조.   #1. datetime.date 이 클래스의 객체는 년월일 정보를 담고 있는 객체이다. 특정한 날짜의 객체를 만들려면 년, 월, 일을 전달해주면 되고, 오늘 날짜를 얻으려면 today() 를 이용한다.   today() 에서는 현재 작업중인 시스템의 로컬 날짜가 반환되게 된다.   date 객체에서 년, 월, 일 을 추출하려면 객체의 year, month, day 속성을 가져오면 된다.  와 같이 한다. 이때 반환되는 값은 int 타입이다.   date.weekday() 는 요일 정보를 숫자로 반환해준다. 월요일이 0이고 순서대로 올라가 일요일이 6이다. date.isoweekday() 도 같은 기능인데, 차이점은 월요일이 1이고 일요일이 7이다.   년월일 정보가 YYYY-MM-DD 식으로 포매팅된 것을 ISO 포맷이라고 하는데 이런 string을 date 객체로 변환하려면  과 같이 하면 된다. 이때 월/일 숫자가 한자리수인 경우 앞에 0 이 붙어있지 않으면 제대로 처리가 안된다. (즉 2020-01-01 은 되지만 2020-1-1 은 처리가 안됨) 반대로 날짜 객체를 ISO 포맷의 문자열로 반환하려면 와 같이 할 수 있다.   date.replace()를 이용하면 객체의 년월일 정보를 바꿀수 있다. date.strftime() 메서드를 이용해 객체를 다양한 방법으로 문자열로 변환할 수 있다. strftime() 포매팅 관련 옵션은 포스트 하단 참조.     #2. datetime.time 이 클래스의 객체는 특정 날짜와 무관한 시간 정보를 담고 있는 객체이다.  특정 시간의 객체를 생성하려면 인자를 직접 전달해주면 된다. 인자를 따로 전달하지 않으면 디폴트 값이 전달된다. hour는 0~23, minute은 0~59, second는 0~59, microsecond는 0~999,999 까지의 정수 값이 허용된다. fold는 서머타임 등 물리적으로 같은 시간에 대해 2가지 다른 시간이 표시될때 조정해주는 값으로 0 또는 1이 사용되는데, 보통은 기본값 0으로 두면 되는듯하다. tzinfo는 시간대 정보를 전달해 준다. 시간대는 별도의 pytz 모듈을 이용해서 지정해주면 편하다. pytz는 따로 설치를 해주어야 한다. pytz에서 지원하는 모든 시간대 정보는 로 확인할 수 있다. 파이썬 날짜/시간 관련 개체는 크게 naive와 aware 객체로 구분할수 있다. 간단히 말하면 시간대 정보를 담고 있으면 aware, 시간대 정보가 담겨있지 않으면 naive 객체이다. 그런데 naive 객체는 시간대 정보가 없어 혼란을 초래할 수 있으므로 가능하면 모든 객체를 시간대를 지정해 aware 객체로 만들어 주는것이 좋다. 그리고 특별한 경우가 아니면 모든 시간대는 UTC 로 통일하고, 특정 지역 사용자에게 보여질때 해당 시간대로 변환해서 보여주는것이 좋다고 한다.     어떤 time 객체의 시, 분, 초, 마이크로초 및 시간대 정보는 각 속성을 입력하면 확인할 수 있다. 여기서 나머지는 int type으로 숫자를 반환하고, tzinfo는 pytz.timezone 객체를 반환한다. 만약 시간대의 이름을 문자열로 얻고싶다면 tzname() 메서드를 호출하면 된다.   date 객체에서와 마찬가지로 time.replace()를 이용하면 time 객체의 시간 정보를 바꿀 수 있다.   time.isoformat() 메서드는 시간 정보를 ISO 포맷의 문자열로 출력해준다. 다만 날짜 데이터와 달리 시간 데이터의 ISO 포맷은 읽기에 불편해 실제 사용할 일이 많지 않을것같다.   time.strftime() 은 date에서와 마찬가지로 다양한 옵션으로 사용자에게 읽기 편한 문자열 출력을 제공한다. 옵션은 역시 포스트 하단의 표 참조.     #3. datetime.datetime 이 클래스의 객체는 날짜와 시간 정보를 모두 담고 있는 객체이다.  객체를 생성하려면 으로 생성하면 된다. 생성된 객체의 년월일, 시분초 등 정보는 위에서와 같은 방법으로 확인할 수 있다.   UTC 기준으로 현재 시간을 담은 객체를 생성하려면 아래와 같이 한다. 현재 시각을 생성하는 메서드는 datetime.now() datetime.today() datetime.utcnow()  가 있는데, 아래의 2가지는 naive datetime 객체를 생성하므로 가능하면 아래 2가지보다 now()를 사용하는것이 권장되고, now()를 쓸 때 시간대 정보를 전달해서 aware 객체를 생성하는 것이 좋다.   date와 time 객체가 따로 존재한다면 datetime.combine() 을 이용해서 결합해줄 수 있다. 이때 별도로 시간대 정보를 전달해줄 수 있다.   또 datetime 객체에서 날짜 정보를 따로 객체로 만들려면 date()을 이용한다. 시간 객체는 time() 혹은 timetz() 이용하면 되는데, 후자는 시간대 정보를 포함하고 있다.   date나 time 객체에서와 동일한 방법으로 datetime.replace() 를 이용하면 날짜와 시간 정보를 수정할 수 있다. datetime.weekday()와 datetime.isoweekday()는 date 객체에서와 동일하다.   datetime.strptime() 을 이용하면 strftime() 에서 사용하는 포매팅 옵션을 이용해서 datetime 객체를 만들어줄 수 있다.   date나 time 과 동일하게 datetime.strftime() 을 이용해서 다양한 포맷으로 출력할 수 있다. 옵션은 포스트 하단 참조,     #4. datetime.timedelta 이 클래스의 객체는 두 날짜 혹은 시간의 차이를 나타내는 객체이다. 객체를 생성하려면 days, seconds, microseconds, milliseconds, minutes, hours, weeks 인자를 전달해주면 된다. 디폴트값은 0 이다. 실제 객체에 저장되는것은 days, seconds와 microseconds 이고, 나머지 인자들은 내부적으로 계산이 이루어져 변환된다. timedelta 개체끼리는 사칙연산과 크기 비교, 절대값 산출 등 기본적인 수학적 처리가 제공된다.   실제로는 timedelta 에 인자를 직접 전달하기보다는 두 datetime 객체의 차이를 구하는데 이용될 것이다. datetime 객체의 차이를 구하면 자동으로 timedelta 객체가 반환된다. datetime 객체끼리 더하는 연산은 지원되지 않아 TypeError 가 발생한다. 위의 예처럼 과거 날짜에서 현재 날짜를 빼는 연산도 가능하다. 그러나 과거 날짜에서 현재 날짜를 빼는것이 논리적으로 어떤 의미인지 명확하지 않고 계산도 복잡해지므로, 항상 현재에 더 가까운 날짜에서 오래된 날짜를 빼는것이 좋을 것같다.   datetime 객체에 timedelta 객체를 더하거나 빼서 특정 시간 이후나 이전의 시간을 구하는것도 가능하다.     이상으로 datetime 모듈 관련 주로 많이 쓰게 되는 내용들을 정리해 보았다. 아래는 strftime() 포매팅 관련 옵션.   %a 현재 로케일에 따른 요일 이름 약자 Sun, Mon, …, Sat (en_US); So, Mo, …, Sa (de_DE) %A 현재 로케일에 따른 요일 이름 Sunday, Monday, …, Saturday (en_US); Sonntag, Montag, …, Samstag (de_DE) %w 10진수로 표현된 요일 번호 (일요일이 0, 토요일이 6임) 0, 1, …, 6 %d 10진수로 표현된 날짜 (한자리수인 경우 0이 앞에 붙음) 01, 02, …, 31 %b 현재 로케일에 따른 월 이름 약자 Jan, Feb, …, Dec (en_US); Jan, Feb, …, Dez (de_DE) %B 현재 로케일에 따른 월 이름 January, February, …, December (en_US); Januar, Februar, …, Dezember (de_DE) %m 10진수로 표현된 월 (한자리수인 경우 0이 앞에 붙음) 01, 02, …, 12 %y 앞 2자리수룰 제외하고 10진수로 표현된 년도 (한자리수인 경우 0이 앞에 붙음) 00, 01, …, 99 %Y 10진수로 표현된 년도 0001, 0002, …, 2013, 2014, …, 9998, 9999 %H 10진수로 표현된 시간 (24시간제) 00, 01, …, 23 %I 10진수로 표현된 시간 (12시간제) 01, 02, …, 12 %p 현재 로케일에 따른 오전, 오후 표시 AM, PM (en_US); am, pm (de_DE) %M 10진수로 표현된 분 (한자리수인 경우 0이 앞에 붙음) 00, 01, …, 59 %S 00, 01, …, 59 %f 10진수로 표현된 마이크로초 (자리수에 따라 0이 앞에 붙음) 000000, 000001, …, 999999 %Z 시간대 (시간대 정보가 없는 naive 객체인 경우 None). (empty), UTC, EST, CST %j 10진수로 표현된 년중 날짜 (1월1일이 001 임) 001, 002, …, 366 %U 10진수로 표현된 년중 주수 (일요일을 한 주의 첫날로 정함. 처음으로 일요일이 포함된 주가 01주, 일요일이 포함되지 않은 날짜들이 속한 그 전주는 00주임) 00, 01, …, 53 %W 10진수로 표현된 년중 주수 (월요일을 한 주의 첫날로 정함. 처음으로 일요일이 포함된 주가 01주, 월요일이 포함되지 않은 날짜들이 속한 그 전주는 00주임) 00, 01, …, 53 %c 현재 로케일 설정에 따라 요일과 년월일, 시간을 출력 Tue Aug 16 21:30:00 1988 (en_US); Di 16 Aug 21:30:00 1988 (de_DE) %x 현재 로케일 설정에 따라 년월일을 출력 08/16/88 (None); 08/16/1988 (en_US); 16.08.1988 (de_DE) %X 현재 로케일 설정에 따라 시간을 출력 21:30:00 (en_US); 21:30:00 (de_DE)    

DigitalOcean에서 Nginx와 Gunicorn 오류 해결 

2020. 03. 16 IT/컴퓨터 > 네트워크
이전 글들: DigitalOcean에 Django 프로젝트 배포하기 (1) DigitalOcean에 Django 프로젝트 배포하기 (2) DigitalOcean에 Django 프로젝트 배포하기 (3)   이전의 DigitalOcean 에서 배포하기 순서대로 했다면 웬만하면 문제없이 배포가 되었을 것이다. 그러나 여러 가지 이유로 인해서 에러가 난다면 다음의 내용을 참고해 보자. 내가 겪은 에러 위주로 몇가지만 정리해 보았다.   #1. Nginx 가 Django 프로젝트 대신 디폴트 화면을 보여주는 경우   이 경우에는 외부에서 정상적으로 Droplet 의 IP에 접근은 가능하지만, Nginx에서 프로젝트의 디렉토리를 찾지 못하는 경우이다. 이전에 만들어주었던 서버 블록에서 잘못된 내용이 있는지 확인하고 수정해 준다. 대개는 IP주소나 디렉토리 경로에 오타가 있는 경우일 것이다. 확인하고 수정해 주자.   #2. Nginx에서 413 오류가 나는 경우. 이 블로그를 처음 가동할때 내가 겪었던 에러는 파일 크기 관련한 에러였다.  블로그에 연동된 CK 에디터를 통해서 글을 작성하면서 이미지를 업로드하는데, pythonanywhere에서는 분명히 정상적으로 작동했는데 DigitalOcean 에서는 계속 413 에러가 나면서 안 되는 것이다. CK에디터 창에서는 413 에러라고만 나오니 왜 저런 에러가 나는지 알기가 어려웠는데, 검색해 보니 파일 크기 관련된 문제인 것 같았다. 우선 를 통해서 에러의 내용을 파악해볼 수 있다. 에러가 난 경우 마지막 에러가 난 시점의 기록을 잘 보면 'client intended to send too large body' 라는 내용을 볼 수 있다. 이것은 Nginx 서버의 업로드 제한 용량 디폴트 값이 1MB 라서 발생하는 일이다. 역시 위에서와 같이 서버 블럭에 파일 용량 제한 옵션을 명시해 주어야 한다. 꼭 서버 블럭이 꼭 아니더라도 특정 디렉토리 블럭에 명시해줄 수도 있지만, 사이트 전체적으로 적용하기 위해서는 서버 블럭에 적어주면 편한것 같다.   위에서 client_body_buffer_size는 request 가 들어왔을 때 처리 가능한 용량이고, client_max_body_size 는 실제로 업로드될 수 있는 파일 크기의 상한선을 지정해 주는 것이다. client_max_body_size 는 실제 업로드를 허용하고 싶은 만큼 지정해 주어야 하지만 client_body_buffer_size는 꼭 그럴 필요는 없는것같다. 정상적으로는 들어온 요청을 메모리에서 처리하지만, buffer 보다 큰 요청이 들어오는 경우 경고를 띄우고 나서 요청이 다 처리될 때까지 디스크에 임시 파일을 만들어 처리한다고 한다. 실제로는 다량의 이미지를 지속적으로 업로드하는 사이트가 아닌 경우에는 동작에 느껴지는 차이는 거의 없다고 한다. client_body_buffer_size를 필요 이상으로 크게 설정해 주는 경우 DDoS 공격에 취약해질 수 있는 약점이 있다고 하므로 너무 크게 잡아줄 필요는 없는것같다. 일단 이미지 등 올리기에는 10MB 정도면 적당해서 저렇게 설정해 주었는데, 사이트 사정에 맞게 조절해주면 될 것 같다.   #3. Django 앱 동작 중 Nginx에서 500 Internal server error 오류가 나는 경우 500 에러는 상황이 워낙 다양해서 원인을 한 가지로 정리하기는 쉽지 않다. 다만 이 경우는 Nginx 자체보다는 거의 대부분 소스 코드의 동작 오류라고 한다. Nginx 와 gunicorn 설정이 잘 되었는데도 오류가 난다면 Django 세팅이나 코드에 문제 있는 부분이 없는지 살펴보자. 처음 이 블로그를 설치하고 배포하니, 로컬 환경에서는 분명히 정상적으로 모든 기능이 동작하는데 서버에서는 첫 화면은 잘 보이다가 글을 작성하고 저장하려고 하면 꼭 internal server error 500가 나오는것이다. 소스 코드는 잘 작동하던것을 그대로 배포한 것이라서 static 파일 문제인지, 디렉토리 퍼미션 문제인지, 자바스크립트 문제인지, 온갖 설정을 다 바꾸고 유저를 새로 만들었다가 없앴다가 퍼미션을 풀었다 잠궜다가 삽질을 수없이 하고, 그후에 도저히 안돼서 Droplet을 삭제하고 처음부터 다시 해 보기도 했는데.. 거의 미쳐버릴뻔 했는데 알고보니 허무하게도 로컬 환경에만 설치돼있고 서버에 설치하지 않은 패키지를 Django 에서 불러들이려고 해서 발생하는 문제였다. 디렉토리 퍼미션은 원래 주어진 대로 하면 아무 문제 없으니 특별한 경우 아니면 안 건드리는게 좋겠다. 구체적으로는 프로젝트에 포함된 CK 에디터의 image_backend에 pillow 가 설정돼있는데 pillow 패키지를 설치하지 않은것 (포스트의 썸네일 생성용으로 CK에디터가 자체적으로 pillow를 이용해 이미지를 처리한다).  로 설치해 주고 나니 거짓말처럼 정상 작동하는 것을 보고 거의 기절할뻔 했다.  사실 pillow 는 이미지 처리할때 필요해서 안 그래도 설치하기는 했어야 하는데..이 블로그 프로젝트 할 때 로컬환경에서 가상환경을 구성해놓지 않으니 이런 사단이 난다. 가상환경을 꼭 먼저 구성해놓고 프로젝트를 하자.   #4. Nginx가 502 bad gateway 오류를 내보낼때 502 오류는 Nginx가 들어온 요청을 제대로 처리를 못 해 주는 경우에 발생하는데, 대개 소켓 등 설정 관련된 경우가 많다고 한다.  를 이용해 원인을 파악해볼 수 있다.    라고 나오는 경우 gunicorn.sock 을 찾을 수 없다는 뜻인데, 처음에  파일 안에  라고 적어주어야 하는 것을 gunicorn.sock 이 아닌 gunicorn.socket 이라고 적었다가 엉뚱한 파일이 만들어져서 이런 오류가 난 적이 있다. 시스템의 핵심부에 이상한 파일이 한번 만들어지면 수작업으로 지우기도 어렵고 뭔가 알수 없는 연계된 오류를 연달아서 뱉어내므로 가급적이면 처음 설정할때 실수하지 않도록 하자. 이것때문에도 Droplet 을 갈아 엎었다. 도대체 몇번을 갈아 엎은건지..ㅎㅎ   이런 오류가 나기도 한다는데, 이것은 개인적으로 겪어 보지는 못했다. systemd가 Gunicorn 의 소켓은 만들어 놓았는데, 퍼미션 문제로 Nginx가 그 소켓에 접근을 못 하는 등의 문제라고 한다. 접속한 유저가 sudo 권한이 있는지, 명령어를 내릴 때 sudo 를 붙여서 내렸는지 등을 확인해 보아야 한다.   를 이용하면 해당하는 파일과 그 상위 디렉토리의 퍼미션 현황을 쭉 보여 주는데, 적절하게 설정되었는지 확인해보고 퍼미션을 조정해주면 된다. (다만 DigitalOcean에서 정상적으로 배포된 경우라면 퍼미션을 손댈 필요가 있는 경우는 없었다)     기타 문제 해결에 도움이 되는 명령어들 위의 명령어들을 이용해서 Nginx 또는 gunicorn 관련된 로그들을 확인해볼 수 있다.   뭔가 설정을 바꿔 주었다면 다음 명령들을 이용해서 nginx 또는 gunicorn 을 재기동해 주어야 한다. 특히 Django 프로젝트의 소스 코드를 바꾼 경우에도 gunicorn 을 다시 기동해 주지 않으면 변경사항이 반영되지 않으므로 꼭 restart gunicon 을 해 주자.   이외에도 크고작은 에러 메세지가 많았는데 다 정리하지는 못했다. 사실 웬만한 에러 메세지는 구글에 물어 보면 거의 다 찾을 수 있다. 그나마 프로그래밍은 비교적 독학하기가 쉬운 점이 구글에 물어보면 비슷한 사례를 많이 찾을수 있다는 것이다. 이런식으로 하나씩 하면 체계는 좀 없는 것이 문제지만.. 어쨌든 DigitalOcean 은 비교적 친절하게 튜토리얼도 잘 되어 있고 설정이 크게 어려운 부분은 없는 것 같다. 로컬에서 잘 작동하는것을 확인한 프로젝트라면, 내가 오타 등 실수만 하지 않으면 깔끔하게 배포가 가능한 것 같다.

파이썬 가상환경이 필요한 이유와 사용법 (venv, virtualenv) 

2020. 03. 11 IT/컴퓨터 > Python
  파이썬에서 프로젝트를 진행할 때는 각각의 프로젝트 별로 가상환경을 만들어서 진행해 주는 것이 좋다. 사실 프로젝트라 할만한 것을 제대로 진행해 보기 전, 책을 보면서 코어 파이썬 문법을 배우고 예제만 따라해 보는 수준에서는 왜 굳이 귀찮게 가상환경을 설치하는것인지 잘 이해가 되지 않았다. 처음 파이썬을 배울때 아나콘다(Anaconda) 배포판을 이용해서 파이선을 설치했는데, 아나콘다를 설치하면 수백가지의 패키지가 같이 설치된다. 이후로도 이것저것 배우면서 설치한 패키지들이 많이 있었는데, 이렇게 한번 설치해 놓으면 패키지를 아무거나 필요한 대로 불러서 쓸 수 있는데 굳이 왜 가상환경을 만들어서 이미 설치했던 패키지를 또 설치하라는건지 이해가 되지 않았다. 그런데 Django를 배우면서 직접 프로젝트들을 한두 개 진행하다 보니 왜 가상환경이 필요한지 저절로 알게 되었다.    1. 프로젝트를 배포하면 원격 서버에 따로 패키지들을 설치해 줘야 하는데, 내가 이 프로젝트만을 위해서 설치한 패키지들이 어떤 것들이 있는지 알 수가 없었다. 내가 설치한 아나콘다 배포판에는 기본적으로 수많은 패키지가 포함되어 있고 그 환경에 내가 추가로 설치한 패키지들이 뒤섞여 있는 상태이다.  한편 배포하려는 서버에는 처음에는 아무것도 깔려 있지 않기 때문에 필요한 것들을 설치해 주어야 하는 상황이다. 그런데 유료로 사용하는 서버에 공간만 차지하고 쓰지도 않을 패키지들을 설치할 필요는 없으니 딱 내가 사용한 패키지들만 설치를 해야 하는데 그걸 구분하는게 문제였다. 물론 어떤 패키지를 설치할 때마다 따로 적어 놓는다든지 할 수도 있겠지만, 프로그래밍을 배운다는 입장에서 그런 원시적인 짓을 할수는 없지 않나. 그리고 pip 명령을 이용해서 어떤 패키지를 설치하면 딱 그 패키지만 설치되는게 아니고 딸려 있는 여러가지 이름이 다른 패키지들도 자동으로 설치가 되는 경우가 많기 때문에, 그런것들까지 다 확인한다는것은 사실상 불가능하다. 그리고 서버에 일일이 그 패키지들을 설치해주는 것이 혹시 가능하다 하더라도 정확히 내가 사용했던 버전과 버전이 맞는지 일일이 확인해야 할텐데, 그걸 수작업으로 한다는것도 역시 원시적이고 말이 안된다. 그래서 미리 가상환경을 만들어 내가 실제로 이 프로젝트에 사용한 패키지들만 설치를 해야 할 필요성을 느꼈다.   2. 그리고 시간이 지남에 따라 패키지들이 업데이트되는데, 이것저것 업데이트를 하다 보면 서로 의존적인 패키지들 사이에 버전이 맞지 않아 호환이 되지 않는 경우들이 생긴다. 그렇다고 특정한 프로젝트 하나를 위해 언제 지원이 끊길지 모르는 예전 버전의 패키지를 로컬에 계속 유지할 수도 없고, 패키지가 업데이트되어 호환성 문제가 생길 때마다 프로젝트의 코드를 일일이 수정하는 것도 실질적으로 불가능하다. 아예 마음먹고 프로젝트를 메이저 버전업을 하면서 갈아 엎어버리는 경우라면 모를까, 구 버전에서 잘 작동하는 프로젝트의 코드를 개별 패키지들의 마이너 버전업 때마다 체크하고 수정하는 것도 불가능하고, 가능하더라도 시간낭비인 경우가 많을것이다. 그래서 한 프로젝트를 위해 확실히 작동하는 버전의 여러 패키지들을 한데 모아서 관리하기 위해서도 가상환경이 필요하다.   3. 파이썬 버전 자체가 다른 환경인 경우도 있겠다. 사실 나는 처음부터 파이썬 3로 입문해서 로컬에서 작업할 때는 파이썬 3만 쓰기 때문에 문제가 없지만, 프로젝트를 배포하려는 서버에는 파이썬 2만 설치된 경우도 있고, 2와 3이 같이 있는 경우도 있는 등 다양한 환경이다. 그런데 예를 들어 최신버전의 Django 같은 경우에는 파이썬 2에서는 아예 동작을 하지 않기 때문에 명시해서 파이썬 3를 설치해 주고 파이썬 3에 맞는 가상환경을 설정해 주지 않으면 프로젝트 구동 자체가 불가능하다.   이외에도 여러가지 이유가 있을 수 있겠지만, 하여튼 연습으로 끄적거리는게 아니라 제대로 구동되는 프로젝트를 목표로 하는 경우라면 프로젝트와 그 프로젝트에 사용된 패키지들은 언제나 한 묶음으로 움직이는 것이 좋겠다. 가상환경을 구성해 주는 유틸리티도 여러가지가 있는 모양이지만, 아래의 virtualenv 혹은 venv 정도면 일단은 무난히 사용할 수 있는것 같다.   #1. Virtualenv 이전 버전의 파이썬부터 많이 사용하던 프로그램이다. 를 이용해서 설치하고, 그 virtualenv 를 이용해서 가상환경을 만들고 관리한다. 프로그램이 설치되면 식으로 원하는 환경의 이름을 지정해서 구동해 주면 가상환경이 만들어진다. 가상환경에 필요한 파일들이  현재 위치 아래의 project_env 라는 디렉토리 안에 설치가 되는 식이다. 특정한 디렉토리에 설치하려면 절대 경로를 전부 입력해 주면 된다. 경험상은 프로젝트 루트 디렉토리 바로 아래에 만들어주면 관리가 편한것 같다.   만들어진 가상환경을 활성화하려면 activate 명령을 입력해야 한다. 리눅스의 경우는 경우에는 project_env/bin 아래에 activate 명령어 파일이 존재하므로 다음과 같이 입력한다. 성공적으로 활성화가 된 경우에는 프롬프트 앞에 (project_env) 와 같이 가상환경의 이름이 표시되는 것을 볼 수 있다.   윈도우의 경우는 project_env/scripts 폴더 안에 activate 명령어 파일이 존재하므로 아래와 같이 입력하면 활성화가 된다. 마찬가지로 프롬프트 앞에 가상환경의 이름이 보인다.   이후 pip list 를 입력해 보면 기본 파이썬만 설치된 리스트를 확인할 수 있다. 가상환경에서 빠져나오는 경우에는 를 입력하면 된다.   #2. Venv 그런데 virtualenv 는 동작이 좀 불안정한 경우가 많은것 같다. 정확히 원인이 뭔지 아직 찾아내지 못했지만 내가 사용하는 데스크탑만의 문제라고 생각했는데 DigitalOcean 에서 만든 우분투 서버에서도 비슷한 일이 일어나는걸로 봐서는 뭔가 virtualenv 설치 과정이나 activation 과정에서 문제가 있는것 같은데.. Activate를 해도 (가상환경이름) 이 보이지 않는 경우도 있고 (이때 pip list 해보면 파이썬만 설치되어 있는 것으로 나오는 걸로 봐서는 뭔가 작동은 하는데 정상 작동이 아닌것같다), 이런 경우에 pip install 을 이용해서 무슨 패키지를 설치하면 설치가 되긴 되는데, 가상환경에 설치되지가 않고 바깥의 원래 환경에만 설치되는 현상이 발생한다. 즉 설치 후 pip list 를 해 보아도 가상환경 안에는 설치한 패키지가 추가되어 있지 않고, 다시 pip install 을 해 보려고 하면 패키지가 이미 설치되어 있다는 메세지만 나온다.  그리고 이런 현상이 일단 발생하면 deactivate를 해도 이전의 환경으로 돌아가지 않는다. Deactivate 해도 아무 에러 메세지는 발생하지 않는데, 이후에 pip list 해보면 정상적으로 가상환경이 종료되었다면 원래 환경에 설치된 수많은 패키지들이 보여야 하는데 하나도 안 보이고 기본 파이썬 패키지만 보인다. 일단 문제의 원인을 찾지 못해서 다른 대안을 찾아 보았는데, 다행히 파이썬 3.4 부터는 venv 라는 패키지가 기본으로 포함되어 있어서 따로 virtualenv를 설치하지 않아도 가상환경을 이용할 수 있다.   venv 를 이용해 가상환경을 만들려면 다음 명령어를 사용한다.   가상환경 활성화 시키는 방법은 virtualenv 와 동일하다. 리눅스: 윈도우:   venv 를 이용해 가상환경을 만든 경우에는 virtualenv 에서와 같은 문제를 아직 겪지 않았다. 어차피 파이썬에 내장된 기능이라면 굳이 virtualenv를 추가로 설치하는 번거로움을 피할 수도 있으니 요즘은 주로 venv 를 이용하고 있다.   #3. 가상환경에서의 패키지 관리 우선 로컬에서 가상환경을 활성화시키고 필요한 모든 패키지들이 설치되었으면, 어느 환경에서든 같은 패키지들이 한 묶음으로 설치되도록 requirements.txt 를 만들어 주는 것이 좋다. 일일이 손으로 적는게 아니라  를 입력하면 현재 환경에서 설치된 모든 패키지들의 이름과 버전이 명시된 requirements.txt 라는 파일이 자동으로 만들어진다.  그리고 원격 서버에서 이 파일을 이용해 패키지들을 일괄 설치하려면 여기서 -r 은 requirement 를 설치할때는 붙이라고 되어 있는데 공식 documentation 을 보아도 아주 속시원한 설명은 없는 듯하다. 각종 옵션에 이런것들이 많은데 다소 심오한 파이썬 언어의 코어에 가까운 이야기들이라 일단은 그냥 시키는대로 하자..   위의 명령어로 필요한 패키지들을 일괄 설치해 주면 로컬과 원격지에서 똑같은 환경으로 작업이 가능하다. 다만 requirement.txt 를 이용한 설치 동작은 서로 의존적인 패키지들간에 간섭이 있어 잘 설치되지 않거나 에러가 나는 경우도 종종 있는 것 같다. 이런 경우에는 프로젝트 안의 패키지들을 pip list 로 다시 확인해서 잘 설치되지 않은 패키지들은 번거롭지만 수작업으로 설치해줘야 할 때도 있다.   사실 처음에는 좀 번거롭다고 생각되기도 했는데, 익숙해지니 가상환경을 만들지 않고 작업하는 것은 생각하기 어렵게 되었다. 깔끔한 작업 환경을 위해서 항상 가상환경을 먼저 구성하도록 하자.

DigitalOcean에 Django 프로젝트 배포하기 (3) 

2020. 03. 06 IT/컴퓨터 > Django
우분투 서버에 Django 프로젝트 배포하기 마지막편. DigitalOcean에 Django 프로젝트 배포하기 (1) DigitalOcean에 Django 프로젝트 배포하기 (2)   이제 본격적인 서버 구동을 위해 Nginx 및 Gunicorn 세팅을 해 주어야 한다. 다른 세팅을 해 주기 전에 gunicorn 이 정상적으로 작동하는지 확인해본다. 다음 명령어로 서버를 구동하고, 브라우저에서 droplet 의 IP를 입력하고 8000번 포트로 접속했을 때 index 화면이 나와야 한다. 다만 아직 서버에서 정상적으로 static 파일 설정을 하지 않았으므로 css 파일을 찾을 수 없어서 스타일링은 정상적으로 적용되지 않을 것이다. 위에서 '프로젝트' 는 wsgi.py 파일이 들어있는 Django app 디렉토리 이름이다. 실제 자기가 만들어 준 app 디렉토리 이름으로 적어 준다. 해당 디렉토리 내에 Django가 자동적으로 생성한 wsgi.py 에 설정되어 있는 설정을 적용해서 gunicorn 을 구동하는 것이다.   정상적으로 구동이 된다면 이제 파이썬 가상환경에서 빠져나오기 위해서 를 입력한다. 프롬프트 앞의 (deploy_env) 가 사라진 것을 확인할 수 있다.   이제 리눅스 서버에 systemd 소켓과 서비스 파일을 만들어 준다. (systemd는 리눅스의 서비스 관리자 같은 것으로 이 부분에 대해서는 더 공부가 필요할듯) 해당 파일들을 작성하게 되면 시스템 부팅시에 gunicorn 소켓이 생성되고, 외부에서의 connection을 기다리고 있다가 connection 이 발생하면 systemd가 자동으로 gunicorn 에게 해당 처리를 맡기는 동작이 발생한다. 우분투에 내장된 nano 에디터를 이용해 socket 파일을 작성해주기 위해 다음 명령어를 입력한다.   에디터가 열리면 아래의 내용을 입력하고 저장해준다. (nano 에디터 내에서 도움말이 나오긴 하는데 저장은 Ctrl + O, 종료는 Ctrl + X 이다)   처음 작성할때 [Socket] 섹션에 gunicorn.sock 부분을 gunicorn.socket 이라고 잘못 입력해놓고 서버 구동이 되지 않아서 어디가 잘못되었는지 못 찾고 한참을 헤맸다. 한번 잘못된 파일이 생성되니 쉽게 복구가 되지 않고 결국 이것저것 건드리다가 완전히 망해서 아예 Droplet 을 삭제하고 처음부터 다시 해야 했다. 오타에 주의하자.    마찬가지로 nano 에디터를 이용해 systemd 서비스 파일을 만들어 준다.     여기서도 sitemaster를 자기 id로, django-deploy-exercise 는 자기 프로젝트 root 디렉토리 명으로, '프로젝트' 는 자기 Django app 으로 적절히 적어 주자.   이제 정상적으로 Gunicorn 소켓 파일이 생성되어 구동할수 있다. socket을 구동하면 /run/gunicorn.sock 라는 소켓 파일이 만들어지게 된다. 다시 강조하지만 이걸 gunicorn.sock 이 아니라 gunicorn.socket 이라고 적었다고 droplet 전체를 갈아 엎었으니 조심하자.   다음 명령어로 Gunicorn socket 을 구동하자.   정상적으로 소켓이 작동하는지 다음 명령어로 확인할 수 있다. 정상 동작중이라면 active 라는 상태 표시가 보일 것이다.   다음으로 gunicorn.sock 파일이 정상적으로 생성되었는지를 다음 명령어로 확인해 본다. 아래 줄과 같은 출력이 나오면 정상이다.   에러 메세지가 나는 등의 상황에서 자세한 동작 로그를 보려면 를 입력해보면 확인할 수 있다.   현 상태에서는 gunicorn.socket 만 활성화되어 있고, 소켓이 아직 아무 연결을 수신하지 않은 상태이므로 gunicorn.service 는 아직 활성화되지 않은 상태이다. 를 입력해서 확인해볼 수 있다. 위와 같이 inactive(dead) 라고 나올 것이다. 이때 curl 을 이용해서 연결 신호를 보내서 정상 작동하는지 확인해볼 수 있다. 다음 명령어를 입력하면 index 페이지의 html 내용이 수신되어 화면에 출력되는 것을 볼 수 있다. 이후  를 다시 확인해 보면 와 같이 active 상태가 되면서 아래에 자세한 동작 로그가 나오는 것을 확인할 수 있다.   에러가 있는 경우에는  을 입력해서 세부 내용을 확인해볼 수 있다.   경험상 이 단계에서의 에러는 대부분 gunicorn.socket 혹은 gunicorn.service 파일 내의 사소한 오타에 의한 것이었다. 파일을 잘 확인해 보고 수정해야 할 부분이 있는지 확인해 보자. 파일을 수정한 경우에는  명령으로 gunicorn 을 재기동해 주어야 변경사항이 제대로 반영된다.     마지막 단계로 Nginx 서버의 설정을 해 준다. 다음 명령으로 Nginx의 서버 블록을 sites-available 디렉토리 아래에 만들어 주어야 한다.     우선 포트는 특별한 일이 없으면 80번으로 설정하고, 아래에 server_name 에는 현재 작업중인 Droplet의 IP 주소를 적어 준다. 나중에 커스텀 도메인을 사용하는 경우는 도메인 이름으로 바꾸어 주어야 한다. 이것은 나중에 다뤄 보겠다. 그 아래의 client_body_buffer_size와 client_max_body_size 는 블로그나 게시판에 글을 올리는 등 사용자가 업로드하는 파일들의 크기에 관한 설정인데, Nginx의 기본 설정은 최대 파일 용량 제한이 1MB로 되어 있어 이보다 큰 파일을 업로드하는 경우 에러가 나게 된다. 프로젝트의 사정에 맞게 적당히 설정해 주자. 아래의 favicon 관련 설정은 인터넷 익스플로러 등 브라우저에서 HTTP 헤더가 로딩되기 전에 /favicon.ico 경로에서 에 파비콘 파일을 요청하는 경우에 Nginx 에 에러를 일으킨다고 하는데 이를 무시하기 위한 설정이다. 같은 서버 블록 아래에 static과 media 파일 경로도 설정해 준다. location 을 명시해 주고 실제 파일시스템 상의 경로를 적어 주는데, static 파일의 경우 현재 Django 프로젝트에서 /static/ 으로 들어온 요청은 실제로는 staticfiles 디렉토리에서 서빙하도록 설정되어 있어 위와 같이 'alias' 를 이용해 설정해 주었다 (이전 편 settings.py 참고). Media 파일의 경우는 경로 이름이 media로 실제 디렉토리 이름과 같아서 직접 'root' 를 설정해 /media/ 로 들어온 요청을 '/home/sitemaster/django-deploy-exercise/media/' 에서 서빙하도록 한 것이다.  맨 아래 root 경로 ('location /') 요청에 대한 설정도 위와 같이 작업해 준다.     파일을 저장 후 작성한 파일을 sites-enabled 디렉토리에 링크해 준다. 설정이 제대로 적용되었는지를 다음 명령어로 확인해 본다.   위와 같은 메세지가 보이면 잘 설정된 것이다.   에러가 없다면 다음 명령어로 Nginx를 재기동해 준다.   마지막으로 방화벽을 Nginx 에 대해 열어 주기 위해 다음 명령어를 입력해 준다. 동시에 테스트용으로 썼던 8000번 포트는 더이상 사용하지 않을 것이므로 방화벽에서 허용해 주었던 설정을 삭제한다.   이제 Droplet의 IP 주소로 가 보면 Django 프로젝트의 index 화면이 정상적으로 보일 것이다. 고생해서 만든 프로젝트의 첫 화면이 어디서나 접속 가능한 상태로 뜨는 것은 언제 봐도 감동적이다.   여기까지 하면 DigitalOcean 에서의 배포는 정상적으로 끝난 것이다. 커스텀 도메인을 가지고 있는 경우 도메인에 연결해 주는 작업이 필요한데, 그것은 다음 글에서 다루고 있다. DigitalOcean 에 나의 도메인 연결하기

DigitalOcean에 Django 프로젝트 배포하기 (2) 

2020. 03. 05 IT/컴퓨터 > Django
DigitalOcean에 Django 프로젝트 배포하기 (1)   #10. Droplet이 성공적으로 만들어졌다면 등록한 이메일로 root 계정의 초기 암호가 배달된다. DigigalOcean에서 제공하는 콘솔을 이용하거나 별도의 SSH 클라이언트를 이용해서 만들어진 droplet 의 IP 주소를 이용해 접속을 한다. SSH 인증을 사용했다면 추가적인 절차가 필요하지만 일단 지금은 password 방식으로 로그인하기로 한다. Login 프롬프트에 root 를 입력하고 처음 주어진 암호를 입력하면, 암호를 리셋하라는 프롬프트가 뜨니 새 암호를 설정해 준다.   #11. 이후 명령으로 우분투 서버 업데이트 사항이 있는지 확인하고 업데이트를 적용해 준다.    업그레이드 가능한 패키지가 있는 경우 명령으로 업그레이드도 같이 해 주는 것이 좋겠다. 업그레이드 중 다음과 같은 메세지가 나오면서 이미 설치된 프로그램에 대해 어떻게 할 것인지 묻는 경우가 있는데, 특별한 경우가 아니라면 그냥 현재 설치된 버전을 유지하는 것으로 선택하자.   업그레이드 후 를 실행하면 필요 없게 된 파일들을 제거해준다.     #12. 새로운 유저를 생성해준다. Root 계정으로 진행해도 되지만, 프로젝트마다 다른 id를 사용하는 것이 편리한 점이 있고, root 계정은 권한이 너무 커서 의도치 않게 치명적인 실수를 저지를 위험이 있으므로 봉인해 두고 새로 만든 계정으로 프로젝트 배포를 진행하는 것이 좋겠다. 식으로 원하는 아이디를 추가하고 패스워드를 설정해 준다. 일단 지금은 sitemaster 라고 지정해 주었다. 이후 이름과 전화번호등 사용자 정보를 입력하는 프롬프트가 뜨지만 입력하지 않아도 아이디 생성은 된다.   #13. 생성된 sitemaster 에 관리자 권한을 주어야 한다. 를 입력해준다. 이렇게 하면 관리자 권한을 얻게 되는데, 이렇게 처리해 준 사용자는 실행하려는 명령어 앞에 sudo 라는 명령어를 덧붙여서 관리자 권한의 작업을 할 수 있다.   #14. 우분투에는 UFW firewall 이라는 방화벽이 포함되어 있어 이를 이용할 수 있는데, 이것과 별도로 DigitalOcean 사이트에서도 방화벽 설정을 할 수 있다. 두 가지 방화벽을 같이 사용하는 경우 충돌이 있을 수 있기 때문에 둘 중 하나만 사용하는 것을 권장하고 있다. UFW를 이용하기 위해서는 설치된 다른 app 들을 ufw 허용 리스트에 등록해 주면 되는데, 명령어로 설치된 app 들을 확인하고, 명령으로 해당 앱이 방화벽을 통과할 수 있도록 설정해 준다.  그 후 명령으로 방화벽을 활성화해 주면 된다. 현재 방화벽의 상태를 명령으로 확인할 수 있다. OpenSSH 는 우리가 만든 서버와의 SSH 통신을 가능하게 해 주는 서비스이므로 방화벽을 통과할 수 있게 등록해 준다.     #15. 이제 새로 생성한 id 로 다시 접속한다. 새로 세션을 열어 접속해도 되고, 명령을 입력해 사용자를 전환해도 된다. - 다음에 빈칸이 하나 있으므로 정확히 입력하자. (su: switch user)   #16. 우선 파이썬 관련된 패키지, postgresql 과 nginx, curl 등 서버 관련된 패키지들을 설치해 준다. 우분투 서버에는 파이썬 2와 3이 모두 설치되어 있지만, Django 버전 3은 파이썬 3.6 이후만 지원하므로 가급적 최신 버전의 장고와 파이썬 3을 이용하는 것이 좋겠다. 현재 프로젝트는 DB를 Sqlite3 를 이용하고 있어서 postgresql 관련 패키지는 당장은 필요 없지만, 향후 프로젝트가 커져서 데이터베이스를 이전할 경우에 대비해서 설치해주었다. 사실 sqlite 도 개인 블로그용 데이터를 저장하는데는 충분하고, 파일 하나로 전체 DB를 다 옮기는 것이 가능해서 소규모 프로젝트에는 편리한 점도 있기 때문에 아직까지는 굳이 바꿀 필요를 못 느끼고 있다. 그리고 이전에 Heroku에서 postgresql 을 설정하다가 골머리를 앓은 기억이 있어 일단 지금은 건드리지 않으려고 한다..   명령으로 설치해준다.     #17. 이제 새로운 Django 프로젝트를 위한 가상환경을 만들어주어야 한다.  명령으로 pip를 최신버전으로 업그레이드해 주고,  명령으로 가상환경을 만들어 주는 virtualenv 패키지를 설치한다.   명령어로 GitHub 에 미리 올려둔 프로젝트를 다운받자. 프로젝트가 성공적으로 clone 되어 해당 프로젝트의 디렉토리가 생성된 것이 확인되면 그 디렉토리로 이동한다.   식으로 해당 프로젝트의 가상환경을 만들어준다. virtualenv 명령 뒤의 가상환경 이름은 다른 이름들과 관련되지 않는 독립된 이름이므로 임의로 지정해 주면 된다. 일단 deploy_env 라고 지정해 주었다. 가상환경이 만들어졌으면 명령을 통해 가상환경을 활성화시켜준다. 명령 프롬프트 앞에 (deploy_env) 와 같이 만들어준 가상환경의 이름이 표시되면 정상적으로 활성화된 것이다.       이유는 잘 모르겠는데 virtualenv 가 잘 작동하지 않는 경우가 있다. 패키지를 설치해도 가상환경 안에서 보이지 않거나, 아니면 반대로 가상환경에서 pip list 를 해 봐도 밖에 있는 패키지들이 전부 보이거나 하는 등의 이상 동작이 생기고 python 2와 python 3가 섞여서 가상환경 안에서도 python 3 버전을 이용하려면 python3 명령어를 이용해야 하는 등..이럴 때는 virtualenv 대신 venv를 사용해 보면 해결될 때도 있다.   venv는 파이썬 3버전에는 포함되어 있는 것으로 알고 있지만, 어쩐 일인지 DigitalOcean 에서 제공하는 우분투 서버에서는 다음 명령을 이용해서 설치해 주어야 한다. venv를 설치해 주고 나면 다음 명령어로 가상환경을 만들어 준다. 환경이 만들어 졌으면 activation 하는 방법은 virtualenv 와 같다.       #18. 이제 Django 및 필요한 패키지들을 가상환경 내에 설치해 준다. 가상환경이 활성화되지 않은 경우 파이썬3 버전의 패키지를 설치하려면 꼭 pip3 을 이용해 설치해야 하지만, 일단 파이썬 3 버전의 가상환경이 활성화된 후에는 설치 프로그램 이름이 pip3 이 아닌 pip 이므로 명령어도 pip3 이 아니라 pip install program 식으로 해야 한다. 경우에 따라서 가상환경 내의 pip가 구 버전인 경우가 있으므로, 우선 를 실행해 pip를 최신 버전으로 업그레이드 해 준다.   이후 로 django, gunicorn 을 설치해준다. psycopg2-binary 는 Postgresql 과 python 을 연결해주는 어댑터 패키지로, postgresql 을 당장 사용하지 않는 경우는 설치하지 않아도 되겠다. 어떨때는 위의 패키지들을 동시에 설치하려고 하면 퍼미션이 없다는 등의 오류를 내면서 가상환경 내에 설치가 되지 않는 경우도 있는데, 이때는 각각의 패키지를 따로 설치해 보면 설치가 되는 것 같다.   프로젝트에 사용된 다른 패키지를 requirement.txt로 freezing 해 놓은 경우에는 로 해당 패키지들을 설치해준다.   Django 프로젝트의 root 디렉토리는 manage.py 가 존재하는 디렉토리이고, root 디렉토리 아래에 프로젝트 메인 디렉토리에 들어가면 settings.py, wsgi.py, urls.py 등의 파일이 있을 것이다. 여기서 settings.py 에 설정을 추가해주어야 한다. 우선 서버에서의 접근을 허용해야 하므로 만들어진 droplet 의 IP 주소를 ALLOWED_HOST에 추가해 주어야 한다. 커스텀 도메인을 사용하는 경우 www.example.com, example.com 등으로 도메인도 같이 추가해 준다. Nginx 를 이용해서 서비스를 구동하는 경우 'localhost'도 추가해 주어야 한다. 127.0.0.1은 Django 내장 테스트용 서버인 runserver 를 구동할 때 쓰인다.   같은 settings.py 파일 안에 css, js 및 image 파일 등의 static 파일들이 어디에 위치하는지 Nginx에게 알려주기 위해서 STATIC_ROOT 설정을 추가해 준다.   나중에 블로그 글에 업로드될 이미지등을 저장하는 media 폴더도 같이 설정해준다.   settings.py 파일을 저장해 주고, 최초로 데이터베이스에 migration 을 해 주기 위해 다음 명령어들을 입력한다. manage.py 가 있는 프로젝트 루트 디렉토리에서 해야 한다.   프로젝트를 관리할 관리자 ID를 다음 명령어로 만들어준다.   프로젝트 내에 있는 static 파일들을 STATIC_ROOT에 지정한 디렉토리로 모아주기 위해 다음을 실행한다. 이제 Django 쪽에서의 준비가 다 되었다. 프로젝트가 잘 뜨는지 테스트를 해 보기 위해 Django 의 내장 테스트용 서버인 runserver를 이용해서 프로젝트를 띄워 본다. runserver가 이용하는 8000번 포트를 개방하기 위해 ufw 설정을 다음과 같이 update 해 주고   다음 명령으로 프로젝트를 구동한다. 이런 화면이 보이면 서버가 구동된 것이고, 이 상태에서 외부 브라우저를 통해    프로젝트 IP:8000을 입력했을 때 프로젝트의 index 화면이 보이면 제대로 작동하는 것이다.   그렇지만 지금은 테스트용 runserver에서 구동한 것이므로 이 상태로 서비스를 할 수는 없고, 본격적으로 서비스를 구동하기 위해서는 웹 서버인 Nginx 와 파이썬 WSGI (web service gateway interface) 서버인 gunicorn 세팅을 해 주어야 한다. 다음편에.. DigitalOcean에 Django 프로젝트 배포하기 (3)

DigitalOcean에 Django 프로젝트 배포하기 (1) 

2020. 03. 05 IT/컴퓨터 > Django
새로 블로그를 시작하면서 호스팅을 디지털오션으로 옮겼다. 이전 버전 블로그는 pythonanywhere.com 에서 배포를 했었다. pythonanywhere는 서버쪽 세팅을 알아서 다 해 주기 때문에 특별히 신경쓸 것 없이 하라는 대로 설정만 해 주면 배포가 가능하다. 초보자로서 시행착오를 많이 겪지 않고 처음 장고 프로젝트를 배포하기에 좋았지만, 조금 더 직접 서버 관련된 세팅을 공부해 보고 싶기도 했고, 다른 호스팅 서비스들은 어떤지 궁금하기도 해서 새 프로젝트는 새로운 곳에서 배포를 해 보기로 했다.  DigitalOcean 은 클라우드 서비스 중에서도 개발자들을 위해 좀 더 특화된 곳이라고 하는데, 아마존이나 구글, 마이크로소프트같은 대형 사업자는 아니지만 나름 입지를 갖춘 곳이다.  이번에도 복기 및 향후 프로젝트 배포를 위해서 참고할 겸 DigitalOcean 에서의 Django 프로젝트 배포 과정을 정리해 보았다.   #1. DigitalOcean.com 에 접속하면  보이는 화면. 별도 계정을 만들거나 구글이나 GitHub 계정에 연동해서 사용할 수 있는데, 지금 프로젝트를 진행하고 있는 GitHub 계정에 연동된 계정을 만들어 접속하였다.   #2. GitHub 계정을 이용해서 로그인한 경우에는 자동으로 github 계정 이름이 프로젝트 이름으로 뜬다. 프로젝트는 필요하면 새로 만들어주어도 된다. 디지털오션에서는 서비스하는 독립된 하나의 클라우드 서버를 Droplet 이라고 부른다. 첫 화면에 보이는 왼쪽 메뉴 바에서 droplet을 선택하면 현재 만들어진 Droplet 현황을 볼 수 있고, 아직 하나도 만들어지지 않은 경우에는 새로 만들 수 있는 화면이 나온다.   #3. 현재 이미 만들어 둔 Droplet들이 있는 경우 관리할 수 있는 메뉴가 뜨지만, 하나도 없는 경우 새로 만들 수 있는 메뉴가 보일 것이다. 또는 오른쪽 위의 초록색 Create 버튼을 이용해서 만들 수도 있다.     #4. 서버를 구동하기 위해 원하는 운영체제를 고를 수 있다. 선호하는 운영체제가 있다면 선택해 주면 된다. 가장 많이 쓰는 Ubuntu 18.04 버전을 선택하였다.     #5. 이용 요금제를 선택해야 한다. 아쉽게도 무료 체험판 서버는 만들 수 없다. Pythonanywhere 같이 간단히 시험할 수 있는 무료 서버를 구동할수 있으면 좋을텐데. 일단 세팅을 해 보고 되는지 안 되는지 확인은 하고 실제 서버를 구동할 수 있게 하면 좋을것 같다. 하지만 기본 플랜은 월 5달러이니 큰 부담은 없다. 그것도 시작하자 마자 5달러가 결제되는 것이 아니고, 리소스 사용분만큼만 조금씩 누적되어 금액이 올라간다. 중간에 droplet 을 없애거나 하면 그 때까지 쓴 만큼만 비례해서 계산되는것 같으니 잠깐 실험해 보는 정도로는 1~2달러 아래로 해결이 가능할 듯하다.  어쨌든 결과적으로 세팅이 매우 쉽고 매끄럽게 돼서 결국은 돈 내고 실제 서버를 돌리게 될 것같긴 하다. 별 시행착오 없이 쉽게 진행이 된다. 가장 저렴한 매월 5$ 짜리 플랜으로도 간단한 블로그를 운영하는 데는 아무 문제 없으니 마음에 든다면 5달러짜리로 시작하면 되겠다.   아래쪽에 block storage 를 추가할 수 있는 옵션이 있는데, 당장 저장소 공간이 부족할 일은 없으므로 나중에 해도 될 것 같다.     #6. 데이터 센터는 각각 테스트해보지는 않았지만 싱가폴이 거리가 가까워 조금이라도 전송 속도가 빠르지 않을까 싶어 싱가폴로 선택해 보았다.    #7. 서버 관리자 인증 방법을 선택할 수 있는데, 보안상 SSH key 를 강력하게 추천한다고 하지만 당장은 해킹당할 내용도 없으므로 password 방식을 선택해도 무방하지 않을까 싶다. SSH key 사용하는 법은 나중에 배우기로...새로 배울것이 많다.     #8. 동시에 여러 개의 droplet 을 생성할 수도 있다. 지금은 하나만 선택. Hostname 은 대시보드에 표시되는 이름인데, 그냥 알아보기 쉬운 이름으로 임의로 지정해 주면 된다. 서버 설정등에 사용될 일은 없는 완전히 독립된 이름이다. 그 아래로 tag 지정을 해주거나 백업을 활성화시킬 수 있는 메뉴도 있는데, 당장 하지 않아도 상관 없다.   설정을 다시 확인해 보고 맨 아래의 Create Droplet 버튼을 눌러 주면 droplet 이 만들어진다.     #9. 만들어진 droplet 을 클릭해보면 기본 정보가 나오는데, 지금은 다른 것은 건드릴 필요 없고 IP 주소만 알고 있으면 된다. IP 주소 위에 마우스를 가져가면 클릭해서 바로 복사할 수 있도록 되어 있다.   이후의 설정은 만들어진 서버에 SSH 콘솔을 이용해서 접속한 후 진행해 주게 된다.  Droplet 화면의 오른쪽 끝에 있는 Console 을 클릭하면 웹 상에서 바로 접속되는 콘솔 창이 뜨니 이것을 이용해서 작업해도 된다. 그렇지만 써 보니 동작이 약간 버벅대는 것이 쾌적하지가 않아 별로 추천하고 싶지는 않다. PuTTY 등의 전용 SSH 클라이언트를 이용하는 것이 좋을 것 같다.  다만 회사에서 PuTTY를 이용해 Droplet의 IP에 접속하려고 하니 방화벽에 막히는지 접속이 안 되었는데, 이런 경우에 웹 콘솔이 대책이 될수는 있겠다. 하지만 회사에서는 할 일을 하자..   서버 설정은 다음편에. DigitalOcean에 Django 프로젝트 배포하기 (2)

블로그에 카카오톡 공유 버튼 만들기 

2020. 02. 27 IT/컴퓨터 > Web Development
이전 글 - 블로그에 SNS 공유버튼 만들기 블로그에 post 공유용 SNS 만드는 방법을 정리해 본다. 카카오톡은 다른 서비스처럼 그냥 url 링크만으로 처리가 되지 않고 따로 개발자 사이트에 가입해서 설정을 해 줘야 한다.   #1. 카카오 개발자 계정 만들기 Kakao Developers 사이트에 가서 가입후 개발자 등록을 해 준다.   #2. 내 애플리케이션 - 앱 만들기에 들어가 앱의 이름과 아이콘등 기본 설정을 해 주고   #3. 개발용으로 각종 키가 생성된다. Javascript 로 버튼을 구현하기 위해 Javascript 키를 복사해 두어야 한다.   #4. 설정-일반-플랫폼 추가 에 들어가서 '웹' 을 선택하고 도메인을 입력해 준다.   #5. '카카오링크' 라는 서비스를 이용해 링크를 걸어줄 것이다. 버튼이 들어가는 페이지의 <head> 안에 다음 스크립트를 추가해 준다.   #6. 본문에는 다음과 같이 markup 을 만들어 주고,   #7. 별도의 <script> 태그 안에 다음 내용을 추가해 준다.   #8. 위에서 title, imageUrl 및 description 에 쓰여있는 |bs4process, |bs4imagepath, |full_url 필터는 이 블로그에 사용하기 위해 개인적으로 만든 Django custom filter들이라서 다른곳에는 적용이 불가능할 것이다. 해당 내용들을 각 페이지에 맞게 변경해 주면 되겠다. 위의 스크립트는 가장 기본적인 링크 기능만 구현한 것이고, 기타 카카오톡 창에 띄워 줄 여러가지 버튼을 만들 수 있는 옵션들이 있는데 카카오링크 개발 가이드를 참조하면 될 것 같다. 여담으로 카카오톡으로 전송한 링크를 모바일 환경에서 클릭해서 열면 항상 카카오톡 인앱 브라우저에서 열리는데, 검색해본 결과로는 유저가 기본으로 쓰는 브라우저에서 열리는 기능은 일부러 지원하지 않고 있다는 것같다. 인앱 브라우저에서 열리면 카카오톡의 다른 톡들을 볼 수 없어 불편하고 결국 주소를 복사해서 크롬 등에서 열게 되는데, 어차피 사용자가 딱히 인앱 브라우저를 선호할 이유가 없을것같고 괜히 번거롭기만 한데 이 점은 좀 개선해 주면 좋을것같다.   이상으로 블로그에 직접 SNS 공유 버튼을 만드는 방법을 정리해 보았다.

블로그에 SNS 공유버튼 만들기 

2020. 02. 27 IT/컴퓨터 > Web Development
블로그의 글을 퍼갈 수 있는 공유 버튼 만드는 방법에 대해 공부한 내용을 정리해 본다. 요즘은 페이스북, 카카오톡같이 많이 쓰는 서비스들은 URL 만 긁어서 붙이면 알아서 보기좋게 링크를 만들어주는 경우가 많아서 활용도가 높지 않을것 같긴 하지만, 아이콘들이 약간의 장식적 효과도 있고 주소를 긁어 붙이는것보다 클릭 한번에 링크해주는게 편리한 점도 있으니 연습삼아 만들어 보았다. Pinterest 등 다른 서비스도 많지만 우선은 페이스북, 트위터, 네이버, 카카오톡 네개만 만들어 보았다. 요즘은 인스타그램이 대세라고 하는데 인스타는 일단 사진을 찍고 메세지를 작성하는 시스템 특성상 외부 링크를 바로 연결해주는 API는 일부러 제공하지 않는 것같다. 포스트를 바로 이메일로 보내는것은 SMTP를 이용해야 할 것 같은데, 이건 나중에 연구해보고 다시 올려야 할듯. 공유 링크는 2020년 2월 15일 기준.   우선 페이지에 버튼을 만들기 위해 html markup을 만들어 준다:   #1. Facebook Facebook 은 https://www.facebook.com/sharer/sharer.php?u=공유할 URL 을 이용하면 간단하게 공유할 수 있다. 단순히 링크만 걸어도 공유에는 문제가 없지만, 전체 화면으로 창이 뜨면 모양이 예쁘지 않기 때문에 window.open() 을 이용해서 새 창에 띄워 주었다. 위의 는 Django  template에서 현 page의 경로를 받아 오는 방법이다. 참고로 root 경로 이후의 경로만 표시하려는 경우  를 사용할 수 있다. 페이스북은 공유하려는 페이지에 open graph tag이 있으면 해당 내용을 읽어서 미리 보기를 만들어 주므로, 페이지의 <head> 안에 이를 넣어 주면 좋다. Open graph tag의 자세한 내용은 위의 링크에 들어가보면 나와 있는데, <meta> tag 을 이용해서 아래의 내용을 적어 주면 된다.     #2. Twitter Twitter는 보낼때 기본 text와 URL을 같이 지정해줄 수 있다. https://twitter.com/share 경로에 파라미터로 text 와 url 을 적어 주면 기본 tweet 창에 미리 입력된 상태로 공유 창이 뜨게 된다.   #3. Naver 네이버 역시 기본적인 방법은 같은데, 네이버는 URL을 encoding 해 주어야 한다. Encoding 하지 않고 그냥 raw URL로 링크하는 경우 제목이 깨지는 등의 문제가 발생한다. 자바스크립트 내장 함수인 encodeURI()로 인코딩한 주소를 링크해주면 된다.   #4. Kakaotalk 카카오톡은 위와 같이 간단하게 링크를 걸 수가 없고 카카오 개발자 사이트 가입해서 key 를 받는 등의 절차가 필요하다. 카카오톡 버튼 만들기는 별도의 글로 정리하도록 하겠다. 링크-블로그에 카카오톡 공유 버튼 만들기
  • 1
  • 2 (current)
  • 3