django에서 데이터베이스에 Insert하기

클라이언트로부터 파라메터를 입력받아 request 가 날아오면

그 데이터들을 이용하여 따로 query 를 짤 필요 없이 django 내장 함수들을 통해 처리할 수 있는

방법이 있다. (이 방법이 더 쉬운지에 대해서는 개인차에 따라 다르니 본인이 좋다고 생각하는 방법을 사용하면 될듯)

간단한 예시를 들어 설명하자면

1
postForm = PostForm(request.POST)

request 를 받을 함수 내에서 request 데이터를 받아 해당 model 의 Form 객체를 생성한다.

이 때 request 로 넘어오는 파라메터 값의 명칭이 Form에 정의해놓은 명칭과 일치해야 자동으로 값을 입력한다.

1
2
3
4
if postForm.is_valid():
post_obj = postForm.save(commit=False) # true일 경우 바로 데이터베이스에 적용, 현재 유저정보가 담기지 않았기에 not null 제약조건에 걸려 작업이 실패하므로 false
post_obj.owner_id = user_row.id
post_obj.save() # obj.save(commit=True) 와 동일

그 후 Form.save() 라는 함수를 이용하여 DB에 바로 Insert 를 해줄 수 있다.

인자로 commit 이란 것을 설정해줄 수 있는데, True 로 설정해놓았다면 그 즉시 Insert 작업을 시행하고

False 라면 DB 에 넣을 준비가 되어있는 객체를 생성하여 받아올 수 있게된다.

위의 예시의 경우 request 로 받지 않은 별도의 column 값을 입력하지 않아 Insert 에러가 발생하기 때문에

따로 입력작업을 한번 더 거친 후,

obj.save(commit=True) 함수를 통해 Insert 작업을 처리하는 모습이다.

axios 사용 관련 메모

참고 자료 출처 : npm axios

공식 문서를 보면 가장 기본적인 사용 예시로 다음과 같은 방법이 소개되어있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// Optionally the request above could also be done as
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

아무것도 모른 상태로 처음 시도해볼 때는 이정도의 사용법으로도 문제가 없었으나

좀더 깊이 들어갈수록 당연스럽게 여러가지 문제에 부딪히게 되었다.

아무런 설정도 없이 파라메터와 메소드 방식만 담아서 요청을 하니 요청을 받는 프로그램 측에서

당연히 이를 제대로 해석하지 못해 문제가 된 것.

임시방편으로 해결책을 찾아 급한 불은 껐지만 나중에 보니 서버측에서 말그대로 데이터만 받고

양식을 다시 짜올리는 비효율적인 방법을 사용하고있었다.

아이러니하게도 이미지파일 정보를 전송하는 방법을 찾던 도중 보다 확실한 방법을 찾게되었다.

덕분에 코드를 관리하기에도 좀더 용이해졌는데 파라메터가 담긴 폼객체와 설정정보가 담긴 객체를 따로

미리 만들어 axios 요청 메소드에 통째로 포함시키는 방법이 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var data = new FormData(); // 먼저 FormData 객체를 생성
...
data.append('user', loginId);
data.append('password', loginPw);
data.append('title', title);
data.append('content', content);
data.append('image', image); // 순서와 관계없이 원하는 파라메터를 집어넣는다.
// 관련 설정 값을 묶어서 변수로 전달
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
// axios 요청 메소드에 파라메터로 전달
axios.post('url', data, config)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

위와 같은 방식을 취함으로써 가독성도 향상되었을뿐더러 코드 관리도 편해졌고

요청 데이터가 확실한 양식을 갖추어 서버측에서 이를 받았을 시 처리하기도 용이해지는 효과를 얻을 수 있었다.

Django request.body 관련 메모

Django로 POST request 관련으로 간단한 api를 짜던 도중 문제가 발생

Postman이라는 프로그램으로 POST 요청을 하면 정상적으로 데이터가 받아지는데 반해

직접 만든 클라이언트측에서 axios를 통해 POST를 요청하면 QueryDict에 아무런 데이터도 받아지지 않는 현상이 일어났다.

한참을 헤맨 결과 request.POST가 아닌 request.body에 얻어지는 데이터를 직접 출력하여 원인을 발견했다.

1
2
3
4
5
Postman :
b'----------------------------418318425319320142162885\r\nContent-Disposition: form-data;name="user"\r\n\r\n○○○○○\r\n----------------------------418318425319320142162885\r\nContent-Disposition: form-data; name="password"\r\n\r\n○○○○○○○○\r\n----------------------------418318425319320142162885--\r\n'
axios :
b'{"user":"○○○○○","password":"○○○○○○○○"}'

Postman의 경우 기본적으로 여러가지 설정이 되어있어 바로 django에서 이를 해석하고

request.POST를 통해 얻어올 수 있지만 axios를 통해 보낸 경우 아무런 설정을 집어넣지 않은 채로

요청하였기 때문에 문제가 발생했다.

문제의 원인을 찾기가 힘들었을뿐이지 찾고나니 다행히 stackoverflow에서 손쉽게 해답을 얻을 수 있었다.

Django는 JSON 페이로드를 실제로 비 직렬화하지 않으니 직접 body 데이터를 비 직렬화해서 얻어야 한다.

1
2
3
4
import json
...
json.loads(request.body)

Webpack 개발서버에서 async/await 사용하기

참고 자료 출처:
webpack
VELOPERT.LOG

Node.js에서 ES6를 완전히 지원하지 않는 문제로 webpack 개발서버를 실행하는 중 async 관련 syntax 오류로 컴파일하지 못하는 문제가 발생했다.

열심히 자료들을 찾아본 결과 Babel을 이용하면 해결할 수 있다는 것을 알게 되었다.

이미 babel-preset을 적용하여 사용하고 있지만

추가로 아래의 4가지 plugin 설정을 필요로 하는 상황이라 오류가 발생하고 있었다.
transform-async-to-generator
transform-class-properties
transform-regenerator
transform-runtime

다음과 같은 방법으로 plugin을 설치 및 webpack 설정을 함으로 문제를 해결했다.

  • plugin 설치
    기본적으로 이미 webpack과 babel-core, babel-loader, babel-preset-es2015 은 설정되어있다고 가정

    1
    $ npm install --save-dev babel-plugin-transform-async-to-generator babel-plugin-transform-class-properties babel-plugin-transform-regenerator babel-plugin-transform-runtime
  • webpack.config.js 수정

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    module.exports = {
    ...
    module: {
    rules: [{
    test: /\.js$/,
    exclude: /(node_modules)/,
    use: [{
    loader: 'babel-loader',
    options: {
    presets: [['es2015', {modules: false}]],
    plugins: [
    'transform-async-to-generator',
    'transform-class-properties',
    'transform-regenerator',
    'transform-runtime'
    ]
    }
    }]
    }]
    }
    };

CORS 관련 메모

CORS 란?

Cross Origin Resource Sharing 의 준말
서로 다른 도메인 간 리소스가 요청될 시 (cross-domain) 동일 출처 정책 (Same-Origin Policy) 에 관련하여 보안 문제로 간주하고 이를 브라우저에서 차단한다.
CORS를 활성화시키면 CORS는 웹 서버에게 보안 cross-domain 데이터 전송을 활성화하는 cross-domain 접근 제어권을 부여한다.

django project 개발 서버에서의 CORS 설정

django-cors-header 모듈을 사용하면 간단하게 해결 가능

  • pip를 사용하여 모듈을 설치

    1
    $ pip install django-cors-headers
  • setting.py 에 모듈 추가 및 각종 설정

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    INSTALLED_APPS = (
    ...
    'corsheaders',
    )
    MIDDLEWARE_CLASSES = (
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
    )
    # CorsMiddleware가 django의 CommonMiddleware 위로 와야 한다.
    CORS_ORIGIN_ALLOW_ALL = False
    CORS_ORIGIN_WHITELIST = (
    '<YOUR_DOMAIN>[:PORT]',
    )
    # 위와 같이 설정하여 하나의 도메인만 서버와 통신할 수 있도록 허용할 수도 있다.
    # 모두 허용하고 싶다면 whitelist를 작성하지 않고 CORS_ORIGIN_ALLOW_ALL의 값을 True로 설정

django 설치방법

먼저 virtualenv 를 설치 후 가상환경으로 들어간다.
(가상환경 설치방법)

pip 를 따로 설치하지 않았어도 python 가상환경 내에 구성되어있기 때문에 pip를 이용하여 손쉽게 django를 설치할 수 있다.

  • 설치 명령어

    1
    2
    3
    4
    $ pip install django
    최신버젼으로 알아서 설치한다.
    $ pip install django==x.x
    따로 원하는 버젼을 지정하여 설치할 수 도 있다.
  • 프로젝트 생성

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ django-admin startproject 프로젝트명
    프로젝트명/
    ├── manage.py
    └── 프로젝트명
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py
    위와 같은 구조로 파일이 생성된다.
  • 앱 생성
    django에서는 프로젝트 내부의 각각의 기능들을 앱별로 나누어서 관리한다.

    1
    $ python manage.py startapp 앱명칭

그 외 기본적인 django 관련 명령어

  • 개발용 웹서버 실행
    1
    2
    3
    4
    $ python manage.py runserver
    전달인자가 없을 경우 호스트는 127.0.0.1, 포트는 8000을 사용
    $ python manage.py runserver 127.0.0.1:8080
    바꾸고 싶다면 따로 지정하여 명령할 수 있다.

서버가 성공적으로 실행됐다면 브라우저의 url에 127.0.0.1:8000 혹은
localhost:8000 을 입력하여 접근할 수 있다.

  • 데이터베이스 동기화
    django framework 에서 제공하는 도구가 사용하는 데이터베이스 관련작업을 자동으로 진행

    1
    $ python manage.py migrate
  • 최고 권한 이용자 생성

    1
    2
    3
    4
    5
    6
    $ python manage.py createsuperuser
    Username (leave blank to use '*****'): admin
    Email address:
    Password:
    Password (again):
    Superuser created successfully.
  • 사용자의 비밀번호 변경

    1
    $ python manage.py changepassword admin

HTML5 - localStorage

출처: w3schools.com

Local StorageHTML5 부터 지원되는 데이터 저장공간이다.

  • 도메인당 4KB 가 최고용량

LocalStorage

  • 2.5MB ~ 5MB 까지 저장 가능 (브라우저마다 최고용량이 다름)
  • 서버로 전송되지 않음.

사용 예시

1
2
3
4
// Store
localStorage.setItem("lastname", "Smith");
// Retrieve
document.getElementById("result").innerHTML = localStorage.getItem("lastname");

setItem으로 값을 설정하고 getItem으로 가져온다.

1
2
3
4
// Store
localStorage.lastname = "Smith";
// Retrieve
document.getElementById("result").innerHTML = localStorage.lastname;

위와같이 setItem 과 getItem 메소드를 꼭 사용하지 않아도 직접 값을 설정하고 가져올 수 있다.

localStorage의 특징은 텍스트형으로 밖에 저장을 할 수 없는 점이다.

객체를 저장하고자 한다면
JSON.stringify() 를 통해 text로 변환하여 저장해야 한다.

1
2
let object = { text: 'test'};
localStorage.dataName = JSON.stringify(object);

저장된 객체(텍스트로 변환 된)를 불러오려면
JSON.parse() 를 통해 다시 객체로 변환하면 된다.

1
JSON.parse(localStorage.dataName);

localStorage를 초기화 하고자 한다면 아래의 메소드를 사용하면 된다.

1
localStorage.clear();

React.js Component Lifecycle API

출처 : inflearn - VELOPERT의 React.js 강좌

Component Lifecycle API

constructor

1
2
3
4
constructor(props) {
super(props);
console.log("constructor");
}

컴포넌트 첫 생성 시 실행
기본 state 를 설정 할 수 있다

componentWillMount

1
2
3
componentWillMount() {
console.log("componentWillMount");
}

컴포넌트가 DOM 위에 만들어지기 전에 실행
그래서 여기에선 DOM 처리가 불가능

componentDidMount

1
2
3
componentDidMount() {
console.log("componentDidMount");
}

첫 렌더링 마친 후 실행
이 안에서 다른 자바스크립트 프레임워크 연동 및 setTimeout, setInterval 및 AJAX 사용
DOM 처리 가능

componentWillReceiveProps

1
2
3
componentWillReceiveProps(nextProps) {
console.log("componentWillReceiveProps: " + JSON.stringify(nextProps));
}

props를 받을 때 실행
props에 따라 state를 업데이트 할 때 사용하면 유용
내부에서 setState 사용 가능
아직 props를 받지 않은 상태이므로 미리 받을 props를 nextProps를 통해 접근

shouldComponentUpdate

1
2
3
4
shouldComponentUpdate(nextProps, nextStage) {
console.log("shouldComponentUpdate: " + JSON.stringify(nextProps)) + " " + JSON. stringify(nextState);
return true;
}

props/state 변경 시 리렌더링 여부를 결정
실제 사용 시 필요한 비교를 하고 값을 반환해야 한다
(ex: return nextProps.id !== this.props.id)
JSON.stringify 를 사용하여 여러 field를 편하게 비교

componentWillUpdate

1
2
3
componentWillUpdate(nextProps, nextState) {
console.log("componentWillUpdate: " + JSON.stringify(nextProps) + " " + JSON.stringify(nextState));
}

컴포넌트 업데이트 전 실행
여기서 절대로 setState를 사용하지 말 것

componentDidUpdate

1
2
3
componentDidUpdate(prevProps, prevState) {
console.log("componentDidUpdate: " + JSON.stringify(prevProps) + " " + JSON.stringify(prevState));
}

컴포넌트가 리렌더링을 마친 후 실행
여기서도 setState 사용 금지
props/state 가 변경 된 후이므로 변경 이전의 값에 접근하려면 prevProps/prevState 를 통해 접근

componentWillUnmount

1
2
3
componentWillUnmount() {
console.log("componentWillUnmount");
}

컴포넌트가 DOM 에서 사라진 후 실행

Python 가상 환경 설치 (virtualenv)

virtualenv

Virtual Environment의 준말, 가상으로 Python 환경을 만드는 도구
한 시스템 내에서 각 각의 다른버젼으로 이루어진 환경을 분리하여 편하게 관리할 수 있다.

Python 패키지 뿐만 아니라 사용할 Python 버전도 가상 환경으로 분리 가능

Python 설치 후 자체 내장 된 기능으로 손쉽게 생성 가능하다.

  • 설치 확인

    $ python3 -m venv
    (windows의 경우 그냥 python -m venv 로 입력)
    
  • virtualenv 생성 명령어

    $ python3 -m venv 가상환경이름
    
  • 활성화 및 비활성화 명령어

    Mac OS, Linux 계열
    $ source env(가상환경이름)/bin/activate
    
    Windows
    $ env(가상환경이름)\Scripts\activate.bat
    
    비활성화
    deactivate