https://freekim.tistory.com/category/%EC%9C%A0%EC%9A%A9%ED%95%9C%20%ED%8C%81

 

'유용한 팁' 카테고리의 글 목록

 

freekim.tistory.com

 

 이전 블로그에서 Jenkins 설치까지 완료하였다. 만약 Jenkins 설치를 못한 사람들은 이전 포스팅에 가서 Jenkins 를 로컬에서 띄우고 이 포스팅의 내용을 진행하길 바란다.

이제 jenkins 설치가 완료되엇다고 생각하고 github 과 연결한 후 push 가 일어났을 때 jenkins 동작하는 것 까지 진행을 해보자.

 

git Repo 와 git Project 는 미리 생성해두자.

 

Task 설명

1. 젠킨스 도커 컨테이너 설치 (완료)

2. github 프로젝트 연결

3. shell 실행

 

 

[GitHub] Access Personal access tokens 발급

GitHub -> Profile -> Settings -> Developer settings -> Personal access tokens -> Generate new token

 

토큰의 설명을 적어주고 repo 와 admin:repo_hook 을 클릭한 후 Generate token 버튼을 누른다.

발급받은 key 값을 따로 저장해둔다. 이 페이지에서 벗어나면 키를 확인 할 수 없고, 다시 토큰을 재생성 해야한다.

 

 

[Jenkins] GitHub Repo 연결

Jenkins 에 GitHub을 등록시켜주기 위해서 Jenkins 관리 탭에 들어간다 

[Jenkins 관리] -> [시스템 설정] -> [GitHub] -> [Add GitHub Server]

 

시스템 설정에 들어가서 스크롤을 내려보면 GitHub Tab 이 있는데 Add GitHub Server를 눌러서  Git Hub 주소를 추가해준다.

눌러보면 아래와 같은 값을 넣어주는 UI 를 만날 수 있고 Name 을 정해주고 API URL 은 Default 값을 사용한다.

Add 버튼을 눌러서 Credential 을 추가해보자.

Add Button 을 누르면 아래와 같이 팝업이 뜨는데 Kinde 를 선택하고 Secret text를 선택해준다.

그 후 Secret key 에는 GitHub 에서 발급 받았던 Token 을 복붙해주고

ID 에는 GitHub ID 를 입력한 후 Add 버튼을 눌러준다.

Add Button 을 누르면 Credentials 에 좀전에 생성한 `jenkins` Credentials 이 추가된 것을 볼 수 있다. 

그리고 Test connection 버튼을 누르면 'Credentials verified for user ${id} rate limit:.....'  를 확인하고 이제 GitHub repo 와 연동이 잘된 것을 확인 할 수 있다.

 

[Jenkins] Jenkins Project 추가 - GitHub Project 연동

Jenkins 에 Item 을 추가하여 GitHub Project 와 연동해준다.

[]

 

Freestyle project 를 선택 한 후 item 의 이름을 입력한 후 ok 버튼을 누른다.

아래와 같은 창이 바로 뜨는데 GitHub Project를 체크해준 후  GitHub project 주소를 입력해준다.

하단으로 내려서 소스 코드 관리 탭에 Repository URL 에는 git clone 시 사용하는 git 의 URL을 입력해주고 과 Add 버튼을 눌러 Credentials 를 추가해 준다.

아래의 popup 이 뜨는데 Username 과 Password 에 github 의 id와 비밀번호를 입력해 준다. 

 

 

빌드 유발은 Jenkins 빌드 실행 조건을 선택해주는 탭인데 아래의 GitHub gook trigger for GITScm polling 을 선택하면 GitHub에 코드가 푸쉬되면 빌드를 트리거링 하게 된다. 어떤식으로 동작하냐면 GitHub 에서 코드 푸쉬가 일어날 때 webHook 메세지를 Jenkins 에 보내주고, Webhook 메시지를 받을 때 Jenkins에서 빌드를 트리거링 한다.

Build 탭에서 빌드를 하는 여러가지 옵션을 선택할 수 있다. Gradle build 도 할 수 있고

Execute shell 을 사용해서 미리 선언해 놓은 shell script 를 실행하게 할 수 있다. 복사, 파일전송 등 여러가지 동작이 필요한 경우 shell command 를 넣어 구성을 해놓으면 된다.

 

GitHub Jenkins WebHook 설정

일단 WebHook 을 날리기 위해서는 Jenkins에 외부 주소가 필요하다. 현재 docker 의 외부 주소가 없으므로 ngrok 을 통해서 jenkins 외부 주소를 만들어주자. 나같은 경우는 Docker ToolBox 를 사용해 도커 컨테이너를 띄워서 IP 를 직접 입력해줬다.

> ngrok http [포트번호]

아래의 젠킨스 주소를 git 에 넣어주면 된다.

Git Project 우상단의 Setting에 들어간다.

Webhooks Tabl 에 Payload URL 에 

Jenkins URL/github-webhook/ 를 입력한다.

이제 GitHub 에서 push 를 해주면 자동으로 build 로 해놓은것이 동작하는 것을 볼 수 있다.

CI/CD 란?

CI지속적인 통합(Continuous Integration)을 의미한다. 개발자들이 어플리케이션에 코드를 merge 하기 위해서는 변경 사항들이 어플리케이션에 에러를 발생시키지 않도록 반복적인 작업을 통해 확인을 해야 한다. 이는 시간을 소모하게 되면서 여러 개발자들이 같이 일을 하면서 코드 상의 conflict 이 발생할 가능성이 생기는데, 변경사항에 대해 테스트할 것들을 자동화 함으로써 신규 코드의 버그를 빠르게 발견하고 효율적으로 수정할 수 있게 도와 주는 툴을 CI 툴 이라고 표현한다.

CD지속적인 서비스 제공(Continuous Delivery)와 지속적인 배포(Continuous Deployment)를 의미한다. Continuous Delivery은 배포팀에게 코드 릴리즈가 자동화 되는 것을 의미하며,  Continuous Deployment은 변경사항을 반영한 후 자동으로 어플리케이션을 실행할 수 있는 것을 의미한다.

 

JenKin? (젠킨스란?)

젠킨스는 개발 작업을 지원하기 위해 1400가지의 플러그인을 가지고 있는 오픈소스 자동화 서버이다. 자동화 테스트, 프로파일링 툴을 이용한 성능 변화 감시 등 여러가지 기능들을 제공하고, 여러 모듈을 사용하여 참조관계가 있는 layer들을 빌드할 때 파이프라인을 사용하여 구성을 간단히 할 수 있다. 모든 언어의 지속적인 통합과 지속적인 전달 환경을 구축하기 위한 방법을 제공한다. 

 

 

Task 설명

1. 젠킨스 도커 컨테이너 설치

2. github 프로젝트 연결

3. shell 실행

 

 

1. 젠킨스 도커 컨테이너 설치

Kitematic을 실행해주면 컨테이너들이 보이는데 jenkins 컨테이너의 CREATE 버튼을 눌러 설치해 준다.

 

설치가 다 되면 아래의 그림처럼 RUNNING 중인 것을 확인 할 수 있다.

 

> docker ps -a

위의 명령어를 터미널에 실행시켜보면 실행중인 컨테이너를 볼 수 있는데 아래와 같이 컨테이너의 8080 포트가 32769 포트로 연결된 것을 확인 할 수 있다. ( 참고로 나같은 경우에는 window home edition 이라 Docker ToolBox 를 사용해서 컨테이너를 실행하기 때문에 Vmware 의 localhost ip 인 http://192.168.99.100/ 로 jenkins 가 시작되엇다.) 

 

오른쪽 위의 WEB Preview 를 클릭해보면 젠킨스 웹 브라우져로 바로 접근할 수 있다.

 

웹브라우져로 가면 아래와 같이 Admin Password 를 입력하라는 화면을 만나게 된다. 친절하게 위치가 써져 있는데 젠킨스 컨테이너의 터미널를 실행 해서 비밀번호를 복사해서 붙여 넣어주면된다.

jenkins의 터미널에 접속하기 위해서는 아래의 명령어를 실행시켜 주면 되는데 -it 옵션은 STDIN 표준 입출력을 열고 가상으로 접속하겠다는 의미 이다.

> docker exec -it jenkins /bin/bash

터미널을 실행한 후 cat 명령어를 통해 Password 를 출력해보자.

> cat /var/jenkins_home/secrets/initialAdminPassword

위와 같이 key 값이 나오는데 복사해서 붙여 넣고 continue 를 누르면 jenkin에 접속하면서 아래의 화면을 만날 수 있다. 기본적인 플러그인을 설치해주는 왼쪽 옵션을 선택해서 기본적인 것들을 설치해준다. 나는 모두 실

이제 젠킨스 설치가 끝났다.

참고 : Jenkins 업데이트

현재 Jenkins 컨테이너는 2.60.3 으로 더이상 업데이트가 없다. 그래서 아래와 같이 버전이 낮아 Plugin 들이 받아지지 않아서 난장판인 것을 볼 수 있다. 이를 해결해 주기 위해서 Jenkins를 업데이트 해주어야 한다.

1. root 권한으로 컨테이너 진입

> docker exec -it -u 0 jenkins /bin/bash

docker 컨테이너에 root 권한으로 터미널을 실행 시킨다 -u 0 옵션은 루트 권환을 획득 한다.

> cd /tmp
> wget ${jenkins download url}

tmp folder 에 wget 명령어를 통해 jenkins war 파일을 다운 받는다. 난 현재 최신버젼인 2.222.3 을 받았다.

> mv ./jenkins.war /usr/share/jenkins
> chown jenkins:jenkins /usr/share/jenkins/jenkins.war

다운받은 jenkins.war 로 교체를 해주고 권한을 바꿔 준다.

>exit
docker restart "jenkins"

컨테이너에서 나온 후 컨테이너를 재실행 시켜준다.

 

 

About Jenkins 에 들어가면 Jenkins 가 업데이트 된 것을 볼 수 있다.

 

 

생각보다 여러가지에서 막혀서 힘들었다.

다음 포스팅에는 git 과 연동 하여 배포 후 자동으로 작업을 하고 release 까지 하는 내용을 작성하겠다.

 

다음 포스팅 - git, jenkins 연동 

 

 

 

출처 : https://www.redhat.com/ko/topics/devops/what-is-ci-cd

 

CI/CD(지속적 통합/지속적 제공): 개념, 방법, 장점, 구현 과정

CI/CD는 애플리케이션의 통합 및 테스트부터 제공 및 배포까지 전체 라이프사이클에서 지속적인 자동화와 모니터링을 제공합니다. 개념, 차이점, 학습방법(인강)을 보세요.

www.redhat.com

 

Mongo DB 를 다루기위해서 기본적인 커맨드를 정리해본다.

혹시 설치를 못하신 분은 아래의 포스팅을 참고~!

https://freekim.tistory.com/12

 

[MongoDB] 몽고디비(NoSQL)의 개념과 설치 방법 (Window)

이글은 Mongo DB를 하나도 사용하지 않은 상태에서, 한번 사용해보기 위해 간단히 짚고 넘어간 개념들을 정리 한 입문 포스팅입니다. 이글은 정확한 Mongo DB의 스펙을 안내하는 글이 아닙니다. 혹시 정확한 스펙..

freekim.tistory.com

 

1. MongoDB 서버 실행

> mongod --dbpath <DB 경로>

 Command 창을 열어 위의 명령어를 실행해준다. 기본경로에 파일을 만든 사람은 그냥 mongod 만 쳐도 되고 path 를 따로 지정해주고 싶으면 뒤의 --dbpath 와 원하는 경로도 같이 넣어줘서 폴더를 지정할 수 있다.

 

2. MongoDB 쉘 실행

> mongo

서버를 실행해 둔채로 새로운 커맨드창을 여러 위의 커맨드를 실행한다.

아래와 같이 Shell Command 창으로 전환이 되는 것을 볼 수 있다.

 

3. 데이터베이스 생성

> use <DB 이름>

데이터 베이스를 사용하려면 관계형 db에서 database를 만들고 table 을 만들어 주는 것과 똑같이 database 를 먼저 만든 후 collection 을 만들어 주면된다. 

 use 명령어를 통해서 db를 만들어준다. 이 database 를 사용하겟다! 라는 느낌 git checkout 같은 느낌.

> show dbs

현재 생성되어 있는 db들을 보여준다. use database 를 하자마자 show dbs를 하면 안보이는데 collection 을 만들어 주면 그때부터 보이니 당황하지 마시길.

> db

현재의 사용하고 있는 db를 보여준다.

 

4. 컬렉션 생성 및 보기 

> db.createCollection("[COLLECTION_NAME]")

person collection을 예제로 생성한 뒤 show dbs 를 하면 databse db가 생성된 것을 볼 수 있다. 여기서 주의할 점은 db  는 만든 db명이 아니라 현재 db 를 가르키는 느낌으로 사용해주면된다. db를 바꾸고 싶으면 use 로 바꾸고 db.xxx해서 사용하면된다.

옵션도 있는데 필요할 때 찾아보시길..

 

> show collections

현재 생성된 collection들을 보여준다.

5. Document 

insert()

> db.person.insert({"nickname":"freekim", "email":"test@google.com"})

document 는 insert 명령어를 통해 json format 으로 insert 한다. 배열의 형태로 넣으려면 [] 를 사용한다. json format 과 똑같다. 아래서 보면 알 수 있듯이 잘 들어갔다. 

참고로 collection 을 안만들어도 insert 해주면 collection 이 생성되어 들어간다

find()

db.person.find()

find 를 이용하여 collection에 들어있는 값들을 볼 수 있다. 아주 잘 들어가 있다. 

find(query, projection) 메소드는 query 와  projection parameter 를 주어서 원하는 조건의 검색과, 보고싶은 결과 선택도 가능하다. 0은 보기, 1은 안보기 

query 는 필요할 때 인터넷에서 찾아보면 많다. 일단은 빠르게 CRUD 만 해보는 거니 Pass 하도록 하겠다.

 

 

6. 삭제 

Document 삭제

> db.person.remove({"nickname":"freekim"})

nickname freekim 의 document 가 잘 삭제 되었다.

Collection 삭제

> db.[collection 이름].drop()

database 삭제

> db.dropDatabase()

시작 하면서 만든 database db 가 잘 지워졌다.

 

마치며..

Mongodb 의 기본적인 동작들을 해보았다. 개발하면서 필요한 기능이나 쿼리들은 검색을 하면 쏟아질 것이니 일단 가볍게 개발을 시작하고, 필요할 때 찾아 써봐야겠다.

 

Update 와 find 에 대해서 자세한 명령어는 아래의 사이트에 아주 잘 정리되어 있어 굳이 두번 정리하지 않겠다.

https://poiemaweb.com/mongdb-basics-shell-crud

 

 

'MongoDB' 카테고리의 다른 글

[MongoDB] 몽고디비(NoSQL)의 개념과 설치 방법 (Window)  (1) 2020.03.29

 이글은 Mongo DB를 하나도 사용하지 않은 상태에서, 한번 사용해보기 위해 간단히 짚고 넘어간 개념들을 정리 한 입문 포스팅입니다.  이글은 정확한 Mongo DB의 스펙을 안내하는 글이 아닙니다. 혹시 정확한 스펙과 세부 기능을 알고 싶으신 분들은 다른 글을 보시길 바래요~

 

RDBMS vs NoSQL

 DB에는 크게 두가지 종류의 DB가 있다. RDBMS (관계형 데이터베이스) 가 있고 그리고 NoSQL 이 있다. 관계형 데이터베이스는 우리가 처음에 db공부를 했을 때 들었던 MySQL 이 대표적인 관계형 데이터베이스이다. RDBMS 와 NoSQL 의 내가 느낀 가장 큰 차이는 schema 가 있고 없고의 차이 같다. RDBMS 는 처음에 정의한 데이터 스키마에 새로운 필드를 넣어주고 싶으면 테이블을 다시 만들어줘야 되고 정해진 데이터 필드에 맞는 데이터만 넣어줄 수 있지만, NoSQL 은 스키마에 새로운 필드를 넣고싶으면 그냥 추가만 해주면된다.

 RDBMS 는 데이터 중복이 없이 관계를 통해서 데이터를 정의하므로 데이터 무결성 (data integrity)를 보장하여 부정확한 데이터가 있을 위험이 줄어드는 장점이 있지만 솔직히 개발자입장에서 처음에 완벽한 스펙이 기획이 된다면 좋겠지만 필요에 의해서 필드를 넣고 없애고 하는 경우가 빈번하기 때문에 나같은 초보 개발자입장에서는 NoSQL 을 쓰는게 편하다.. 고생을 해보면 마음이 바뀔지도... 동일한 데이터가 여러 컬렉션에 있으면 모두 update 를 해줘야 한다는 단점이 있지만 업데이트는 컴퓨터가하니 일단 기능을 확장하고 할때 스키마를 마음대로 수정할 수 있는 NoSQL 이 개발하기 편한게 편하다..

 

1. 개요

Mongo DB 는 NoSQL 중 하나로 Document Oriented 데이터 베이스이다.

Document Oriented라는 말은 쉽게 말해서 JSON 형식이나 XML 형식으로 DB에 데이터를 저장하는 것을 말한다. Mongo DB 는  key : value 로 이루어진 JSON 의 형태로 데이터를 저장한다.

 

Json 형식 예시

{
  "firstName": "free",
  "lastName": "kim",
  "email": "test@gmail.com"
  "study" : ["com", "game"]
}

 

RDBMS 의 한 row 를 MongoDB 에서는 Document 라고 표현을 하고 table 을 Collection 이라고 한다. 그리고 Collection 들의 집합은 Database 이다. 개념은 동일하고 용어는 좀 다르니 가볍게 생각하자.

 

 

2. MongoDB 장단점

 그래도 쓰는데 장단점은 알아야 하니 가볍게 정리해보고 넘어가겠다. 여러가지 장단점이 있겠지만 그냥 가장 눈에 띄는 장단점을 짚고 넘어가 본다. 사용을 해보면서 느끼는 장단점은 추가적으로 포스팅을 하겠다. 초보자이니 이정도만 알고 바로 시작! 아 그런데 트랜잭션 지원하려면 Replica SET 환경을 구성해줘야 한다는 포스팅이 있다.... 필요할때... 찾아서 해봐야지..

장점

 1. 속도가 빠르다.

 2. 스키마가 없다.

단점

 1. join 사용 불가

 2. 메모리 사용량 큼

 

3. Mongo DB 설치 (Window)

1. MongoDB Communi Server 다운로드 (https://www.mongodb.com/download-center/community)

맞는 os를 눌러서 다운받는다.

2. 윈도우 실행시 자동 으로 Service 가 실행되는게 싫다면 체크해제

3. GUI 툴이 필요 없다면 체크해제

4. 아무일도 벌어지지 않아도 당황하지 마시길.. ProgramFile 에 가보면 잘 설치되어 있는 몽고 DB를 볼수 있다. 설치라기보단 압축 해제해 놓은 수준인듯.

5. 실행을 위한 bin 폴더 경로 환경변수 Path에 추가.

4. MongoDB 실행

  mongod

cmd 에서 해당 커맨드를 입력해주면 아래와 같은 에러를 볼 수 있다. 몽고db가 참조하는 데이터 폴더를 안만들어 줫기 때문이다. 

path 를 지정해주고 mongodb 를 실행한다.

mongod --dbpath 경로

해당 path 에 뭔가가 어마어마 하게 깔린다. 이렇게 mongodb run time 시에 path 옵션을 주어 mongo db 를 실행하게 된다. 

 

보면 local host 의 27017 포트에서 몽고디비가 귀를 기울이면서 듣고 있는 것을 볼 수 있다. 웹브라우져에서 127.0.0.1:27017 쳐보면 아래와 같이 잘돌고 있는게 보인다

종료하고 싶으면 mongod 를 실행한 cmd 창에서 ctrl + c 를 이용하여 종료하면 몽고디비 서비스가 종료된다

 

아래의 명령어를 통해서 Shell 을 실행할 수 있다. 

Shell 을 시작 하기 전에는 mongo DB 서비스를 켜두는 것은 잊지말자.

mongo

 

'MongoDB' 카테고리의 다른 글

[MongoDB] 몽고DB 기본 명령어  (1) 2020.03.31

서버로부터 데이터를 받아올 때 한번에 다 받아올 수도 있지만 데이터가 많을 경우 일부분만 받아와야 할 경우가 생긴다. 예를 들면 대부분의 홈페이지에 게시판을 보더라도 한번에 모든 글들을 리스트로 보여주지 않고 아래 숫자로 page 를 넘기는 식으로 구현되어 있는데 이럴 경우에 사용하는 것이 Pagination 이다. Django RestFramework 에서는 기본적으로 Pagination 를 제공하는데 활용한다면 특별한 구현 필요 없이 자동으로 Page 로 구성이 된 형식으로 결과를 받아볼 수 있다. 아래는 공식 guide book 에서 설명한 pagination 이다. 아래의 링크를 보고 따라해도 되고 간단하니 내가 작성한 글을 따라 해도 된다.

https://www.django-rest-framework.org/api-guide/pagination/

 

Pagination - Django REST framework

pagination.py Django provides a few classes that help you manage paginated data – that is, data that’s split across several pages, with “Previous/Next” links. — Django documentation REST framework includes support for customizable pagination styles. This a

www.django-rest-framework.org

 

1. Setting.py 설정

 아래와 같이 REST_FRAMEWORK 의 Default Pagination Class 를 정의해주면 Pagination 된 결과를 확인할 수 있다.

LimitOffsetPagination 은 Parameter 를 통해 limit값과 offset 값을 줄 수 있는데, 예를 들어 get url 마지막에 limit=40&offset=100 이라고 보내준다면 40개의 데이터를 100번째 부터 달라는 의미이다.

 

Settings.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

무튼 Settings.py 만 정의해주면 아래같이 pagination 된 결과를 받을 수 있다. 아래에서 count field 는 총 Data 의 갯수이고 next 와 previous 는 다음 값과 이전 값의 url 이다. 현재는 default page_size 로 100이 선언이 되었기 때문에 다음 페이지가 필요가 없으므로 next가 null 로 비어 있다. 

2. Pagination Custom

Default 값으로 선언된 Pagination 말고도 직접 나의 Viewset 이나 GenericAPIView 에 내가 정의한 Pagination 을 할당 해 줄 수 있다. 공식 가이드 처럼 PageNumberPagination 를 override 해서 사용해보자. PageNumberPagination 은 파라미터로 Page 번호만 받는 방식이다. 

http://localhost:8000/posts/?page_size=2

views.py 에 PageNumberPagination 을 import 한 후 가이드처럼 LargeResultsSetPagination 이름의 PageNumberPagination 를 override 한 class 를 선언해준다 한번에 가져올 Data 갯수 parameter 로 사용할 page_size 를 정의 해주고 page_query_param 변수에 할당 해준다. max_page_size 는 최대 요청 가능한 page size 값이다.

 그리고 아래와 같이 Vieset 의 pagination_class 에 내가 정의한 LargeResultsSetPagination 를 할당해 준다.

 이러면 끝이다. 결과를 살펴보자. 참고로 GenericAPIView 에도 똑같은 방식으로 pagination_class 가 존재하고 값을 할당해 줄 수 있다.

 

views.py

...
from rest_framework.pagination import PageNumberPagination

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 3
    page_query_param = 'page_size'
    max_page_size = 100

class PostViewset(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    pagination_class = LargeResultsSetPagination

page size 가 3으로 되어있고 Data 는 총 4개이기 때문에 next 에 다음 query 가 있는 것을 볼 수 있다.

 

3. 결과 확인

 next 를 눌러보면 다음 Data set 이 보여진다. 4개이니 next는 없고 이전값인 previous query 가 있는 것을 볼 수 있다.

 

기존에는 단순 results 의 List 만을 가지고 안드로이드의 retrofit 을 사용해서 restAPI 를 호출 하였지만 pagination 을 활용 하니 json 의 구조가 바뀌었다. 안드로이드에 바뀐 구조를 받아오는 restfit 예제도 실습한뒤에 포스팅 하겠다.

내가 쓴 글이 있으면 밑에 댓글 기능을 추가 하고싶은 요구사항이 생기는 것은 당연하다. Comment 자체를 새로운 App 으로 만들어서 구현한 예제도 있는 듯 하나 Post 모델과 Comment 모델을 하나의 app 에서 정의해보는 예제를 구현해 보았다.

 

1. Comment 모델 추가

기존에 사용하던 app 의 post 모델이 선언된 models.py 파일에 아래와 같이 Comment Model을 추가해준다.

text field 하나를 가지고, post field 는 foreignkey 로 선언해준다. foreignkey 에 Post 모델을 넣어주면 comment 는 Post 에 종속성을 가진다.

 

models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    text = models.TextField()
    image = models.ImageField(upload_to="%Y/%m/%d")
    

class Comment(models.Model):
    text = models.CharField(max_length=200)
    post = models.ForeignKey('Post', related_name='comments', on_delete=models.CASCADE, blank=True)

 

2. Serializers 추가

Comment 모델에 대한 직렬화를 해주기 위하여 생성해 놓은 serializers.py 파일에 CommentSerializer 를 정의 해준다.

.model 에 Comment 를 import 시켜주는 것은 잊지말자. CommentSerializer 의 model 에 Comment 를 정의해주고 fields 에는 __all__을 넣어주면 Comment 모델에서 사용하는 모든 field 가 들어간다.

 기존에 PostSerializer 에는 comments field 를 추가해 주는데 하나의 Post에는 여러개의 comment 가 존재할 수 있으므로 many=True 라고 정의 해준다. 

 

Serializers.py

from rest_framework import  serializers
from .models import Post, Comment

class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = '__all__'

class PostSerializer(serializers.ModelSerializer):
    image = serializers.ImageField(use_url=True)
    comments = CommentSerializer(many=True, read_only=True)

    class Meta:
        model = Post
        fields = ('title', 'text', 'image', 'comments')


 

3. Viewset 추가

 View는 가장 쉬운 Viewset 을 사용하여 Comment 의 View 를 정의해보았다.

여기에서도 새로만든 Comment 모델과 CommentSerializer 를 import 하는 것을 잊지 말자.

기존에 선언해 두었던 Postviewset 과 동일하게 CommentViewset 를 정의해줫다.

 

views.py

from rest_framework import viewsets
from .serializers import PostSerializer, CommentSerializer
from .models import Post, Comment

class PostViewset(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

class CommentViewset(viewsets.ModelViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer

 

4. URL 추가

url은 기존에 post 가 쓰고 있었던 Router에 CommentViewset 을 추가 해주기만 하면 바로 동작된다.

알고 있겟지만 viewset 을 사용하면 자동으로 CRUD 에 해당하는 URL 이 정의 된다.

아래에서 추가되는 코드는 router.register(r'comments', views.CommentViewset) 이 것 단 한줄이다.

 

urls.py

router = routers.DefaultRouter()
router.register(r'posts', views.PostViewset)
router.register(r'comments', views.CommentViewset)


urlpatterns = [
    url(r'^',include(router.urls)),
    ...
    

 

5. 실행

실행해보자 실행전에 database migration을 진행한다. 진행 후 server를 실행한다.

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver

http://localhost:8000/ 에 접속하면 기존에는 posts api 밖에 없었지만 comments 까지 추가가 되 있는 것을 볼 수 있다.

posts 를 호출해보면 Post Model에 추가적으로 정의했던 comments field 가 추가된 것을 확인 할 수 있다. 원래는 공백이지만 나는 미리 구현 해놓고 테스트를 해봐서 comment 들이 남아 있는 상황이다.

이제 http://localhost:8000/comments/ 에 들어가보자. 내가 입력했던 comment들을 볼 수 있고 아래의 입력창을 보게 되면 text 와 Post field 가 있다. 

Post field 의 경우 Foreign key 로 선언을 해놓았기 때문에 작성한 Post 들 중에서 선택할 수 있는 UI 를 만날 수 있다.

하나 선택을 해서 입력을 해보자. Post를 누르게 되면 아래와 같이 입력한 comment 가 저장이 되고 post field 에는 post의 PK 값이 들어가게 된다.

Comment 가 잘내려오는지 Post 에 가서 확인해보자. 아래와 같이 내가 선택했던 Post에 댓글이 성공적으로 추가된 것을 확인 할 수 있다.

 

Post Field 에 내가 선언해 놓은 field 들이 더 있어서 보이는 것이니 혼동하지 말자. 

 

서버시나리오에서 빠지지 않는 기능은 이미지를 업로드 하고 보는 기능이다 RestFramework 을 이용하면 ImageField 를 활용하여 쉽게 Image를 업로드 할 수 있다. 기존에 사용했던 Model에 ImageField를 추가해서 사용하는 방법으로 구현을 해보겠다.

 

1. /media 폴더 추가

프로젝트 root directory 에 media folder를 생성한다.

 

2. Setting 및 URL 추가

MEDIA_URL 은 Media 파일에 대해서 고정된 URL 값을 설정할 때 사용 한다. 

MEDIA_ROOT 는 db 에는 파일의 경로만 저장이 되기 때문에 앞으로 파일이 저장될 root 경로를 정해준다.

 

settings.py

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

 

MEDIA_URL 에 대한 요청이 왔을 때 MEDIA_ROOT 에서 일을 처리하게된다.

urls.py

...
from django.conf import settings
from django.conf.urls.static import static
...


urlpatterns = [
	...
]  + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

3. Model 에 ImageField 추가

기존에 선언해 두었던 Post Model에 ImageField 를 추가한다.

upload_to="%Y/%m/%d" 를 사용하면 /Root/년도/월/일 폴더가 생성되어 저장되고

upload_to="String" String 값을 넣어주면 특정 폴더에 저장을 해준다. Image File 이 많아지면서 한 Directory 에 사진이 많아지게되면 성능상의 이슈가 생기므로 Image Path 를 컨트럴을 잘해줄 필요가 생길 수 있다.

models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    text = models.TextField()
    image = models.ImageField(upload_to="%Y/%m/%d")
    #image = models.ImageField(upload_to="image")

 

후에 이미지 업로드를 해보면 아래와 같이 폴더가 생성되어 이미지가 저장된 것을 확인할 수 있다.

 

4. Serializers.py 에 image field를 추가

RestFramework 에서 사용하는 Serializer.py 에 Image field를 추가한다. Meta class의 fields 에 값을 추가한다.

use_url 을 True 로 선언해 주면 output으로 URL String 이 내려오고 False 이면 File Name 이 output 으로 나온다. Default 값은 UPLOADED_FILES_USE_URL 로 set 되어 있는데, 따로 설정안한다면 이 값은 True 값을 가지고 있다.

Serializers.py

from rest_framework import  serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    image = serializers.ImageField(use_url=True)
    
    class Meta:
        model = Post
        fields = ('title', 'text', 'image')

5. DB migrations

DB가 변경되었으므로 migrate해준다.

$ python manage.py makemigrations
$ python manage.py migrate

위의 명령어를 사용해보니 Pillow 라는 앱을 사용해야 되나보다. Pillow 라는 앱이 install 이 안되어있다는 error 메세지가 뜬다. 다행히 HINT 에서 나온대로 pip install Pillow 하니 바로 해결되었다.

설치 후 다시 makemigrations 한 결과이다. 잘 추가 되었다는 메시지를 볼 수 있다.

migrate 까지 완료 시켜주고 서버를 실행해보자

 

결과 확인

Post 창에 Image Field 가 생긴 것을 확인 할 수 있다. 데이터를 입력해주고 POST 를 클릭해보도록 하자.

POST 를 누르게 되면 아래와 같이 내가 입력한 데이터가 잘 들어간 것을 보여준다. 나같은 경우는 upload_to 에 %Y/%m/%d를 넣어줫기 때문에 Image URL Path 가 아래와 같이 형성된 것을 확인할 수 있다.

URL 을 클릭해보면 내가 올린 이미지가 보이는 것을 확인할 수 있다.

 

 서버시나리오를 생각 했을 때 빠질 수 없는 것이 회원가입, 로그인, 로그아웃 시나리오 이다. 한번만 구현을 해본 경험이 있으면 앞으로 꾸준히 쓸 수 있을 것 같아서 정리를 해두려고 한다.

 

로그인을 관리하기 위해서는 Django 에서 제공하는 Django-rest-auth 라는 패키지를 이용하여 구현을 한다. 클릭 하셔서 Docs 를 보고 직접 구현을 하셔도 되고 내가 실습한 예제를 보셔도 된다.

 

1. Django-rest-auth 설치

아래의 패키지를 나의 가상환경에 설치해준다.

$ pip install django-rest-auth

 

2. Setting.py 에 rest_auth 추가

Setting.py 에 rest_auth 를 추가해준다. rest_framework.authtoken 을 같이 설치해준다. Doc에 보면 rest_auth Project 는 django-rest-framework libary 에 dependency 가 있으므로 rest_framework 을 꼭 설치 하라고 가이드 되어 있다. rest_framework 에 대한 예제는 https://freekim.tistory.com/4 에 나와 있다. 참고 하시길.

 

settings.py

INSTALLED_APPS = [
    ...,
    
    'rest_framework',
    'rest_framework.authtoken',
    'rest_auth',
    ...,
]

 참고) rest_framework 이 설치가 안되어 있는경우

pip install djangorestframework

 

3. rest_auth 의 url 을 추가

urls.py

urlpatterns = [
    ...
    url(r'^rest-auth/', include('rest_auth.urls')),
    ...
]

 

4. Database 에 migration 하기 

url 까지 추가가 끝났으면 database 에 migrate 해준다.

$ python manage.py migrate

명령어를 python 명령어를 실행하면 아래의 화면을 볼 수 있다.

5. Registration

 registration 을 해주기 위해서 setting.py 에 아래의 앱들을 추가해 준다. 이 때 social 의 Authentication 을 추가할 수 있으나 이건 나중에 해보기로 하자. 그리고 SITE_ID = 1 도 추가해준다. allauth app이 설치가 안되어 있다면 설치해주자.

pip install django-allauth

Setting.py

INSTALLED_APPS = [
    ...
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'rest_auth.registration',
]

SITE_ID = 1

 

url 도 추가해주자

urls.py

urlpatterns = [
    ...,
    url(r'^rest-auth/', include('rest_auth.urls')),
    url(r'^rest-auth/registration/', include('rest_auth.registration.urls'))
]

 

Setting 에 아래의 configuration 값을 넣어주면 jwt 를 활성화 해줄 수 있는데, 아직 JWT 를 학습하지 않아 나중에 JWT POST 를 작성하면서 더 자세한 설명을 하도록 하겠다. 

 Docs 를 읽어보면 django-rest-auth 는 Default로 Django’s Token-based authentication이지만 JWT(Json Web Token)로 사용 할 수 있게 해주는 것 같다. 나같은경우는 rest api 로 json 으로 응답을 받으니 JWT 는 다음 포스트에 공부해서 바로 사용 해보아야 겠다.

REST_USE_JWT = True

 

이제 API 의 EndPoint 를 살펴보자

<< Basic >>

  • /rest-auth/login/ (POST)

    • username
    • email
    • password

    Returns Token key

  • /rest-auth/logout/ (POST)

  • /rest-auth/password/reset/ (POST)

    • email
  • /rest-auth/password/reset/confirm/ (POST)

    • uid
    • token
    • new_password1
    • new_password2
  • /rest-auth/password/change/ (POST)

    • new_password1
    • new_password2
    • old_password
  • /rest-auth/user/ (GET, PUT, PATCH)

    • username
    • first_name
    • last_name

    Returns pk, username, email, first_name, last_name

<< Registration >>

  • /rest-auth/registration/ (POST)

    • username
    • password1
    • password2
    • email
  • /rest-auth/registration/verify-email/ (POST)

    • key

 

결과 확인

아래의 명령어를 통해 server를 실행 시킨다.

$ python manage.py runserver

1. end point 의 registration 을 먼저 접근해보자. 아래의 페이지를 볼 수 있다.

간단하게 정보를 입력하고 post 를 해보자. (e-mail 은 default로 활성화 되어 있다. 아래와 같이 설정을 바꾸면 변경 가능하다.

ACCOUNT_EMAIL_REQUIRED = False

ACCOUNT_EMAIL_VERIFICATION = 'none'

)

http://localhost:8000/rest-auth/registration/

나같은 경우에는 migrate 이 잘못됫는지 email 이 field 가 없다는 오류가 났었다. allauth 에 뭔가 연관이 있는것 같기도 하고 무튼 다시migrate 을 해주고 실행을 하니 정상 동작 했으나 password 를 너무 짧게 썻었는지 아래의 결과를 얻었다. 기본적으로 비밀번호 길이에 대한 예외처리가 들어가 있나보다. 편하다. 그리고 단순한 password 에 대해서도 예외처리가 되어 있나보다.

 

비밀 번호를 변경해 준 후 post 를 눌러보니 잘안되는 것 같았지만 등록이 되었다. 등록이 되었다면 로그인을 해보자.

localhost:8000/rest-auth/login 을 하면 아래의 화면을 볼 수 있다.

방금 전 registration 한 정보를 입력 시켜주고 POST 하게 되면

아래의 그림처럼 token값이 넘어오면서 오른쪽 상단에 볼 수 있듯이 내가 입력한 id 로 로그인이 되게 된다.

이 밖에 다른 endpoint 들도 test해보시길 바란다.

+ Recent posts