https://code.tutsplus.com/ko/tutorials/how-to-write-your-own-python-packages--cms-26076



나만의 파이썬 패키지를 작성하는 법

by 
Difficulty:IntermediateLength:MediumLanguages:

Korean (한국어) translation by Dae-yeop Lee (you can also view the original English article)

파이썬은 훌륭한 프로그래밍 언어이자 훨씬 더 그 이상입니다. 하지만 파이썬의 약한 점 중 하나는 패키징입니다. 이것은 파이썬 커뮤니티에도 잘 알려진 사실입니다. 패키지의 설치, 임포트, 사용과 생성은 수년 동안 개선되긴 했지만 파이썬을 비롯해 다른 좀 더 성숙한 언어들을 반면교사 삼은 Go와 Rust 같은 새로운 언어와 여전히 같은 수준은 아닙니다.

이번 튜토리얼에서는 자신만의 패키지를 만들고 공유하는 데 필요한 모든 것을 배웁니다. 파이썬 패키지에 대한 일반적인 배경지식이 필요하다면 파이썬 패키지 사용하는 법을 참고합니다.

프로젝트 패키징은 응집성 있는 파이썬 모듈 및 기타 파일들을 가져와 손쉽게 사용할 수 있는 구조로 만드는 과정입니다. 이때 다른 패키지에 대한 의존성, 내부 구조(서브 패키지), 버전 관리, 대상 사용자, 패키지 형태(소스 및/또는 바이너리)와 같이 다양한 사항들을 고려해야 합니다.

Advertisement

간단한 예제로 시작해 봅시다. conman 패키지는 설정을 관리하기 위한 패키지입니다. 이 패키지는 etcd를 이용한 분산 설정뿐 아니라 여러 파일 형식을 지원합니다.

일반적으로 패키지의 내용은 단 하나의 디렉터리에 저장되며(하위 패키지를 여러 디렉터리로 분할하는 경우가 더 많긴 하지만) 때때로 이 경우처럼 자체적인 git 저장소에 저장되기도 합니다.

루트 디렉터리에는 다양한 설정 파일(setup.py는 필수이고 가장 중요한 파일임)이 들어 있으며, 패키지 코드 자체는 일반적으로 패키지와 이름이 같은 하위 디렉터리 및 이상적으로는 tests 디렉터리에 존재합니다. 다음은 "conman" 패키지의 파일 및 디렉터리 구성을 보여줍니다.

01
02
03
04
05
06
07
08
09
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
35
36
37
38
39
40
41
> tree
 
.
 
├── LICENSE
 
├── MANIFEST.in
 
├── README.md
 
├── conman
 
│   ├── __init__.py
 
│   ├── __pycache__
 
│   ├── conman_base.py
 
│   ├── conman_etcd.py
 
│   └── conman_file.py
 
├── requirements.txt
 
├── setup.cfg
 
├── setup.py
 
├── test-requirements.txt
 
├── tests
 
│   ├── __pycache__
 
│   ├── conman_etcd_test.py
 
│   ├── conman_file_test.py
 
│   └── etcd_test_util.py
 
└── tox.ini

setup.py 파일을 살짝 살펴봅시다. 여기서는 setuptools 패키지에서 setup()과 find_packages()라는 두 가지 함수를 임포트합니다. 그런 다음 setup() 함수를 호출하고 매개변수 중 하나에 find_packages()를 사용합니다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from setuptools import setup, find_packages
 
 
 
setup(name='conman',
 
      version='0.3',
 
 
      license='MIT',
 
      author='Gigi Sayfan',
 
      author_email='the.gigi@gmail.com',
 
      description='Manage configuration files',
 
      packages=find_packages(exclude=['tests']),
 
      long_description=open('README.md').read(),
 
      zip_safe=False,
 
      setup_requires=['nose>=1.0'],
 
      test_suite='nose.collector')

보다시피 아주 평범합니다. setup.py 파일은 일반적인 파이썬 파일이라서 이 파일 안에서 원하는 모든 작업을 수행할 수 있지만 이 파일의 주된 작업 setup() 함수를 적절한 매개변수와 함께 호출하는 것입니다. 왜냐하면 패키지를 설치할 때 다양한 도구에서 표준화된 방식으로 이 함수가 호출될 것이기 때문입니다. 다음 절에서 자세한 내용을 살펴보겠습니다.

setup.py 말고도 여기서 보여드리거나 다양한 목적으로 사용될 수 있는 몇 가지 다른 선택적인 설정 파일이 있습니다.

setup() 함수는 다양한 명령을 실행하는 것과 더불어 패키지 설치의 여러 측면을 제어하기 위한 명명된 인수를 여러 개 받습니다. 대부분의 인수는 패키지를 저장소에 업로드할 때 검색 및 필터링에 사용되는 메타데이터를 지정하는 역할을 합니다.

  • name: 패키지 이름(그리고 PYPI에 어떻게 나열될지를 지정)
  • 버전: 적절한 의존성 관리를 유지하는 데 중요합니다.
  • url: 패키지 URL. 일반적으로 깃허브(GitHub) 또는 readthedocs URL에 해당합니다.
  • packages: 패키지에 포함해야 할 서브 패키지 목록. 이때 find_packages()가 활용됩니다.
  • setup_requires: 이곳에 의존성을 지정합니다.
  • test_suite: 테스트 시 실행할 도구

여기서는 long_description을 README.md 파일의 내용으로 설정했는데, 이처럼 한곳에서 필요한 내용을 작성하고 이를 참조하는 것이 좋습니다.

setup.py 파일은 다양한 명령을 실행하기 위한 명령행 인터페이스도 제공합니다. 예를 들어, 단위 테스트를 실행하려면 python setup.py test라고 입력하면 됩니다.

01
02
03
04
05
06
07
08
09
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
running test
 
running egg_info
 
writing conman.egg-info/PKG-INFO
 
writing top-level names to conman.egg-info/top_level.txt
 
writing dependency_links to conman.egg-info/dependency_links.txt
 
reading manifest file 'conman.egg-info/SOURCES.txt'
 
reading manifest template 'MANIFEST.in'
 
writing manifest file 'conman.egg-info/SOURCES.txt'
 
running build_ext
 
test_add_bad_key (conman_etcd_test.ConManEtcdTest) ... ok
 
test_add_good_key (conman_etcd_test.ConManEtcdTest) ... ok
 
test_dictionary_access (conman_etcd_test.ConManEtcdTest) ... ok
 
test_initialization (conman_etcd_test.ConManEtcdTest) ... ok
 
test_refresh (conman_etcd_test.ConManEtcdTest) ... ok
 
test_add_config_file_from_env_var (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_simple_guess_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_simple_unknown_wrong_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_simple_with_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_simple_wrong_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_add_config_file_with_base_dir (conman_file_test.ConmanFileTest) ... ok
 
test_dictionary_access (conman_file_test.ConmanFileTest) ... ok
 
test_guess_file_type (conman_file_test.ConmanFileTest) ... ok
 
test_init_no_files (conman_file_test.ConmanFileTest) ... ok
 
test_init_some_bad_files (conman_file_test.ConmanFileTest) ... ok
 
test_init_some_good_files (conman_file_test.ConmanFileTest) ... ok
 
 
 
----------------------------------------------------------------------
 
Ran 16 tests in 0.160s
 
 
 
OK

setup.cfg는 ini 형식의 파일로서 setup.py에 전달하는 명령에 대한 옵션 기본값을 담을 수 있습니다. 여기서 setup.cfg는 nosetests(테스트 러너)에 대한 몇 가지 옵션을 담고 있습니다.

1
2
3
4
5
[nosetests]
 
verbose=1
 
nocapture=1

이 파일에는 내부 패키지 디렉터리에 들어있지는 않지만 포함시키고 싶은 파일을 담습니다. 일반적으로 이러한 파일로 readme 파일이나 라이선스 파일 및 이와 비슷한 파일들이 있습니다. 중요한 파일은 requirements.txt입니다. 이 파일은 pip가 다른 필수 패키지를 설치하는 데 사용됩니다.

conman의 MANIFEST.in 파일은 다음과 같습니다.

1
2
3
4
5
include LICENSE
 
include README.md
 
include requirements.txt

의존성은 setup.py의 install_requires 섹션과 requirements.txt 파일에 모두 지정할 수 있습니다. pip는 install_requires의 의존성은 자동으로 설치하지만 requirements.txt 파일의 의존성은 자동으로 설치하지 않습니다. 그러한 요구사항을 설치하려면 pip install -r requirements.txt와 같이 pip를 실행할 때 명시적으로 지정해야 합니다.

install_requires 옵션은 메이저 버전 수준에서 최소한의 추상적인 요구사항을 지정하기 위해 고안됐습니다. requirements.txt 파일은 고정된 마이너 버전을 이용해 좀 더 구체적인 요구사항을 지정하기 위한 것입니다.

conman의 요구사항 파일은 다음과 같습니다. 보다시피 모든 버전이 고정돼 있음을 알 수 있습니다. 이는 이러한 패키지 중 하나가 업그레이드되어 conman을 망가뜨리는 변경사항이 도입될 경우 부정적인 영향을 받을 수 있음을 의미합니다.

01
02
03
04
05
06
07
08
09
10
11
PyYAML==3.11
 
python-etcd==0.4.3
 
urllib3==1.7
 
pyOpenSSL==0.15.1
 
psutil==4.0.0
 
six==1.7.3

버전을 고정하면 예측 가능성과 마음의 평안을 확보할 수 있습니다. 이는 많은 사람들이 여러분의 패키지를 서로 다른 시기에 설치하는 경우에 특히 중요합니다. 버전을 고정하지 않으면 각 사용자가 설치한 시점에 따라 서로 다른 버전의 의존성을 얻을 수 있습니다. 버전 고정의 단점은 의존성 개발을 쫓아가지 않으면 오래되고 성능이 형편없으며, 심지어 취약한 의존성에 발목을 잡힐 수 있다는 것입니다.

나는 conman을 2014년에 처음으로 작성했고 크게 신경을 쓰지 않았다. 이제 이 튜토리얼에 쓰기 위해 모든 것을 업그레이드했으며 거의 모든 의존성 전반에 걸쳐 상당한 개선이 있었다.

여러분은 소스 배포판이나 바이너리 배포판을 만들 수 있습니다. 여기서는 둘 다 다루겠습니다.

소스 배포판을 만들려면 python setup.py sdist 명령을 이용합니다. 다음은 conman에 대한 출력 결과입니다.

01
02
03
04
05
06
07
08
09
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
> python setup.py sdist
 
running sdist
 
running egg_info
 
writing conman.egg-info/PKG-INFO
 
writing top-level names to conman.egg-info/top_level.txt
 
writing dependency_links to conman.egg-info/dependency_links.txt
 
reading manifest file 'conman.egg-info/SOURCES.txt'
 
reading manifest template 'MANIFEST.in'
 
writing manifest file 'conman.egg-info/SOURCES.txt'
 
warning: sdist: standard file not found: should have one of README, README.rst, README.txt
 
 
 
running check
 
creating conman-0.3
 
creating conman-0.3/conman
 
creating conman-0.3/conman.egg-info
 
making hard links in conman-0.3...
 
hard linking LICENSE -> conman-0.3
 
hard linking MANIFEST.in -> conman-0.3
 
hard linking README.md -> conman-0.3
 
hard linking requirements.txt -> conman-0.3
 
hard linking setup.cfg -> conman-0.3
 
hard linking setup.py -> conman-0.3
 
hard linking conman/__init__.py -> conman-0.3/conman
 
hard linking conman/conman_base.py -> conman-0.3/conman
 
hard linking conman/conman_etcd.py -> conman-0.3/conman
 
hard linking conman/conman_file.py -> conman-0.3/conman
 
hard linking conman.egg-info/PKG-INFO -> conman-0.3/conman.egg-info
 
hard linking conman.egg-info/SOURCES.txt -> conman-0.3/conman.egg-info
 
hard linking conman.egg-info/dependency_links.txt -> conman-0.3/conman.egg-info
 
hard linking conman.egg-info/not-zip-safe -> conman-0.3/conman.egg-info
 
hard linking conman.egg-info/top_level.txt -> conman-0.3/conman.egg-info
 
copying setup.cfg -> conman-0.3
 
Writing conman-0.3/setup.cfg
 
creating dist
 
Creating tar archive
 
removing 'conman-0.3' (and everything under it)

보다시피 표준 접미어 중 하나가 붙은 README 파일이 누락됐다는 경고가 표시됐는데, 저는 마크다운을 좋아해서 "README.md"를 대신 만들어뒀기 때문입니다. 그 밖의 모든 패키지 소스 파일과 추가 파일이 포함됐습니다. 그런 다음 conman.egg-info 디렉터리에 여러 메타데이터가 만들어졌습니다. 마지막으로 conman-0.3.tar.gz라는 압축된 tar 아카이브가 생성되어 dist 하위 디렉터리에 저장됩니다.

이 패키지를 설치하려면 빌드 단계가 필요할 것입니다(심지어 순수 파이썬으로 만들어져 있더라도). 평소처럼 pip를 이용해 패키지 경로만 전달해도 해당 패키지를 설치할 수 있습니다. 예를 들면 다음과 같습니다.

1
2
3
4
5
6
7
8
9
pip install dist/conman-0.3.tar.gz
 
Processing ./dist/conman-0.3.tar.gz
 
Installing collected packages: conman
 
  Running setup.py install for conman ... done
 
Successfully installed conman-0.3

conman이 site-packages에 설치됐고 다른 여느 패키지와 마찬가지로 임포트할 수 있습니다.

1
2
3
4
5
import conman
 
conman.__file__
 
'/Users/gigi/.virtualenvs/conman/lib/python2.7/site-packages/conman/__init__.pyc'

휠(wheel)은 파이썬 코드와 선택적으로 C 확장을 패키징하는 비교적 새로운 방법입니다. 휠은 egg 형식을 대체합니다. 휠에는 순수 파이썬 휠, 플랫폼 휠, 범용 휠과 같은 몇 가지 유형이 있습니다. 순수 파이썬 휠은 conman과 같이 C 확장 코드가 없는 패키지를 말합니다.

플랫폼 휠에는 C 확장 코드가 있습니다. 범용 휠은 동일한 코드 기반으로 파이썬 2와 파이썬 3에 모두 호환되는 순수 파이썬 휠입니다(2to3조차도 필요하지 않음). 순수 파이썬 패키지가 있고 패키지가 파이썬 2와 파이썬 3(점점 더 중요해지고 있는)을 모두 지원하게 하려면 파이썬 2용 휠 하나와 파이썬 3용 휠 하나를 빌드하는 대신 단 하나의 범용 휠을 빌드하면 됩니다.

패키지에 C 확장 코드가 있는 경우 각 플랫폼용 플랫폼 휠을 빌드해야 합니다. 특히 C 확장이 포함된 패키지의 경우 휠의 커다란 이점은 대상 장비에서 컴파일러 및 지원 라이브러리를 사용할 필요가 없다는 것입니다. 이는 휠에 이미 빌드된 패키지가 포함돼 있기 때문입니다. 따라서 빌드가 실패하지 않으며, 빌드된 패키지를 단순히 복사하는 것이라서 설치 과정도 훨씬 빠릅니다. Numpy나 Pandas 같은 과학 라이브러리를 사용하는 사람들은 이러한 패키지를 설치하는 데 오랜 시간이 걸렸고, 일부 라이브러리가 누락됐거나 컴파일러가 제대로 설정되지 않은 경우 패키지 설치에 실패했을 수 있으므로 분명 이를 반가워할 것이다.

순수 휠 또는 플랫폼 휠을 빌드하는 명령은 python setup.py bdist_wheel입니다.

setup() 함수를 제공하는 엔진에 해당하는 Setuptools는 순수 휠 또는 플랫폼 휠이 필요한지 여부를 자동으로 감지합니다.

01
02
03
04
05
06
07
08
09
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
running bdist_wheel
 
running build
 
running build_py
 
creating build
 
creating build/lib
 
creating build/lib/conman
 
copying conman/__init__.py -> build/lib/conman
 
copying conman/conman_base.py -> build/lib/conman
 
copying conman/conman_etcd.py -> build/lib/conman
 
copying conman/conman_file.py -> build/lib/conman
 
installing to build/bdist.macosx-10.9-x86_64/wheel
 
running install
 
running install_lib
 
creating build/bdist.macosx-10.9-x86_64
 
creating build/bdist.macosx-10.9-x86_64/wheel
 
creating build/bdist.macosx-10.9-x86_64/wheel/conman
 
copying build/lib/conman/__init__.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
 
copying build/lib/conman/conman_base.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
 
copying build/lib/conman/conman_etcd.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
 
copying build/lib/conman/conman_file.py -> build/bdist.macosx-10.9-x86_64/wheel/conman
 
running install_egg_info
 
running egg_info
 
creating conman.egg-info
 
writing conman.egg-info/PKG-INFO
 
writing top-level names to conman.egg-info/top_level.txt
 
writing dependency_links to conman.egg-info/dependency_links.txt
 
writing manifest file 'conman.egg-info/SOURCES.txt'
 
reading manifest file 'conman.egg-info/SOURCES.txt'
 
reading manifest template 'MANIFEST.in'
 
writing manifest file 'conman.egg-info/SOURCES.txt'
 
Copying conman.egg-info to build/bdist.macosx-10.9-x86_64/wheel/conman-0.3-py2.7.egg-info
 
running install_scripts
 
creating build/bdist.macosx-10.9-x86_64/wheel/conman-0.3.dist-info/WHEEL<br>

dist 디렉터리를 살펴보면 순수 파이썬 휠이 생성됐음을 확인할 수 있습니다.

01
02
03
04
05
06
07
08
09
10
11
ls -la dist
 
 
 
dist/
 
total 32
 
-rw-r--r--  1 gigi  staff   5.5K Feb 29 07:57 conman-0.3-py2-none-any.whl
 
-rw-r--r--  1 gigi  staff   4.4K Feb 28 23:33 conman-0.3.tar.gz

"conman-0.3-py2-none-any.whl"이라는 이름에는 패키지 이름, 패키지 버전, 파이썬 버전, 플랫폼 버전, 그리고 마지막으로 "whl"이라는 확장자가 포함돼 있습니다.

범용 패키지를 빌드하려면 python setup.py bdist_wheel --universal처럼 --universal을 추가하기만 하면 된다.

그 결과로 만들어지는 휠은 "conman-0.3-py2.py3-none-any.whl"입니다.

참고로 범용 패키지를 만들 경우 코드가 파이썬 2와 파이썬 3에서 모두 실제로 작동하도록 보장하는 것은 여러분의 책임입니다.

다음의 포스트를 참고하세요! https://valuefactory.tistory.com/565?category=765138

나만의 파이썬 패키지를 작성하려면 많은 도구를 다루고, 여러 메타데이터를 지정하며, 의존성과 대상 사용자에 관해 곰곰이 생각해봐야 합니다. 하지만 그 결과로 얻는 보상은 큽니다.

유용한 코드를 작성하고 이를 적절히 패키징하면 사람들이 이를 손쉽게 설치하고 이로부터 혜택을 얻을 수 있을 것입니다.




00.  プロジェクトアドミン

01.  チーム管理

02.  アカウント管理

03.  コスト管理

  031. AWS

  032. GCP

  033. Azure

04.  ロードマップ

05.  KPI

 

10. 要件定義

11. 概要

12. ユースケース一覧

13. 提案

 

20. 設計

21. 全体

    21-1. 全体シーケンス図

    21-2. 全体システム構成図

    21-3. 全体ネットワーク図

    21-4. 実行アーキテクチャ

    21-5. 開発アーキテクチャ

    21-6. 運用アーキテクチャ

    21-7. DBテーブル定義

    21-8. KVS定義書

22. 共通

   22-1. ログ設計

   22-2. エラーハンドリング

23. Console

   23-1. モックアップ

   23-2. 画面仕様書

   23-3. DBテーブル定義

   23-4. KVS定義書

   23-5. URL定義書

24. Business Logic API

   24-1. エンティティ一覧

   24-2. API仕様書(Swagger

 

25. AI Engine API

  25-1. API仕様書(Swagger

26. SDK

  26-1.

27. 個別

  27-1. SB

  27-2. FR

30.環境

31. 構築メモ

32. 検証評価

  32-1. Kubeless検証

  32-2.

 33. 構築手順

  33-1. Redmine

  33-2.

 34. AWS設定パラメータ

 34-1. Dev環境

   34-2. Staging環境

   34-3. Production環境

34. SQL

36. Config設定(実行するApplicationJsonyamlファイル)

 

40. 開発

41. 開発ルール

41. ローカル開発環境

44. デプロイ手順

 

50. テスト

51. テスト計画

51. テストデータ

56. 単体テスト

57. 結合テスト

58. システムテスト

59. 受入テスト

 

60. 運用

61. 運用ルール

62. 障害管理

64. 分析

  641. KPI分析

65. 手順

67. セキュリティ対応

69. Tools

 

70. プロジェクトマニュアル

71. 一般

  71-1. プロジェクトのルール

  71-2.

 72. dev

   72-1. Python

   72-2. Gitの運用ルール

   72-3.

 73. ops

74. その他

 

80. ドキュメント

81. ドキュメント一覧

82. 内部向け

83. 外部向け

84. 英語

 

90. クライアント

91. SB

  91-1. 受領資料(TeamsURL

  91-2. 納品物(TeamsURL

92. FR

99. 個人作業

https://wayhome25.github.io/etc/2017/11/26/restful-api-designing-guidelines/



(번역) RESTful API Designing guidelines — The best practices

 4 Comments |

RESTful API Designing guidelines — The best practices 번역글 입니다.

views


Facebook, Google, Github, Netflix와 같은 거대한 테크기업은 API를 통해서 개발자들과 프로덕트들이 자신들의 데이터를 사용할 수 있도록 하고, 그들을 위한 플랫폼이 되었다. 비록 당신이 다른 개발자들과 프로덕트를 위한 API를 개발하고 있지 않더라도, 잘 만들어진 API를 갖고 있는 것은 당신의 어플리케이션을 위해서도 좋다.

인터넷에서는 오랫동안 API 디자인을 위한 가장 좋은 방법에 대해서 논의가 이루어졌다. 하지만 통일된 공식적인 가이드라인은 없다.

API는 많은 개발자들이 데이터와 상호작용을 하기 위한 인터페이스이다. 잘 디자인된 API는 사용하기 쉽고, 개발자들의 삶을 편하게 만들어준다. API는 개발자를 위한 GUI이며, 만약 사용하기 혼란스럽거나 설명이 부족하다면 개발자들은 대체물을 찾아서 떠날 것이다. 개발자 경험은 API의 질을 측정함에 있어서 가장 중요한 기준이라고 할 수 있다.


views
API는 무대에서 공연하는 아티스트이고, 사용자는 관객이다.

1) 용어

다음은 REST API에 관련된 가장 중요한 용어들이다.

  • Resource 는 어떤 것의 대표 혹은 객체이다. 이는 관련 데이터를 갖고 있고, 이를 운용하기 위한 메소드 집합을 가질 수 있다.
    예: Animals, schools, employees는 resources이고 delete, add, update는 이 resources들에서 수행되는 operations이다.
  • Collections 는 resources의 집합이다.
    예: Companies는 Company resource의 집합니다.
  • URL (Uniform Resource Locator)은 어느 resource가 어디에 위치할 수 있고, 어떤 action들이 수행될 수 있는지를 나타내는 경로이다.

2) API endpoint

더 이해하기 위해서 Employees를 가진 Companies에 대한 API를 작성해보자. /getAllEmployees 는 employees의 리스트를 response하는 API이다. Company에 관련된 API는 다음과 같은 것이 존재할 수 있다.

  • /addNewEmployee
  • /updateEmployee
  • /deleteEmployee
  • /deleteAllEmployees
  • /promoteEmployee
  • /promoteAllEmployees

그리고 다른 operations을 위해서 아주 많은 API endpoints가 존재할 수 있다. 이는 많은 중복을 포함하고 있고, 이러한 API endpoints는 수가 증가함에 따라서 유지보수가 힘들어진다.

무엇이 문제일까?

URL은 오직 resources(명사)만 포함해야 하며 동사나 actions를 포함해서는 안된다. API path /addNewEmployee 는 action 인 addNew 를 resource 이름은 Employee와 함께 포함하고 있다.

그럼 무엇이 올바른 방법일까?

/companies endpoint는 action을 포함하지 않는 좋은 예시이다. 그럼 이럴 땐 어떻게 서버에게 해당 companies resource에 대해서 add, delete, update와 같은 actions을 수행하도록 알려줄 수 있을까?

이는 동사인 HTTP 메소드 (GET, POST, DELETE, PUT)가 그 역할을 수행할 수 있다.

resource는 언제나 API endpoint에서 복수형 이어야 한다. 그리고 만약 resource의 특정 인스턴스에 접근하고 싶다면 URL에 id를 전달하여 접근할 수 있다.

  • method GET path /companies 는 companies의 모든 목록을 가져온다.
  • method GET path /companies/34 는 company 34의 상세 내용을 가져온다.
  • method DELETE path /companies/34 는 company 34를 삭제한다.

몇 가지 다른 사용 사례에서 resource 아래에 여러 개의 resources가 있는 경우 (예 : 회사의 직원들), API endpoint는 아래와 같을 수 있다.

  • GET /companies/3/employees 는 company 3에 속하는 employees 전체 목록을 가져온다.
  • GET /companies/3/employees/45 는 company 3에 속하는 employee 45의 상세 내용을 가져온다.
  • DELETE /companies/3/employees/45 는 company 3에 속하는 employee 45를 삭제한다.
  • POST /companies 는 새로운 company를 생성하고, 생성된 company의 상세 내용을 리턴한다.

이와 같은 API가 더 일관성 있고 정확하지 않은가?

결론: path는 resources의 복수형을 포함해야 하고, HTTP 메소드는 해당 resource를 대상으로 수행되는 action의 종류를 정의해야 한다.

3) HTTP methods (동사)

HTTP는 resources를 대상으로 수행할 수 있는 몇 가지 타입의 methods를 정의해두고 있다.

URL은 문장이고, resources는 명사이며 HTTP methods는 동사이다.

주요 HTTP methods 는 아래와 같다.

  1. GET method는 resource로 부터 데이터를 요청하며, 어떤 side effect도 발생시켜서는 안 된다.
    예: /companies/3/employees 는 company 3에 속하는 모든 employees를 리턴한다.

  2. POST method는 database에 resource를 생성하도록 서버에 요청하며, 대부분 web form 형식으로 제출된다.
    예: /companies/3/employees 는 company 3에 새로운 employee를 생성한다.
    POST는 멱등성을 갖지 않으며 여러번의 request는 각각 다른 영향을 미친다.

  3. PUT method는 resource를 업데이트 하거나, 만약 존재하지 않는 경우 생성하도록 서버에 요청한다.
    예: /companies/3/employees/john 는 company 3에 속하는 employees collection에 john이라는 resource를 업데이트하거나 생성하도록 서버에 요청한다.
    PUT은 멱등성을 가지며, 여러 번의 request는 같은 영향을 미친다.

  4. DELETE method는 resources 혹은 그것의 인스턴스를 database에서 삭제하도록 요청한다.
    예: /companies/3/employees/john/ 는 company 3에 속하는 employees collection에서 john이라는 resource를 삭제하도록 서버에 요청한다.

4) HTTP response status codes

API를 통해서 클라이언트가 서버에 request를 발생시키면 클라이언트는 해당 request가 성공, 실패 혹은 request 자체가 잘못되었는지 등의 결과를 얻을 수 있어야 한다. HTTP status code는 다양한 시나리오에서의 상황을 설명할 수 있는 표준화된 코드이다. 아래는 HTTP 코드의 주요 카테고리 분류이다.

2xx (Success Category)

해당 status code 들은 request가 정상 수신되고 서버를 통해서 성공적으로 수행되었음을 나타낸다.

  • 200 Ok GET, PUT, POST 성공을 대표하는 표준 HTTP response
  • 201 Created 해당 status code는 새로운 인스턴스 생성 시 리턴되어야 한다. 예를 들면 POST 메소드를 사용하여 새로운 인스턴스가 생성되면 항상 201 status code를 리턴한다.
  • 204 No Content 는 request가 성공적으로 수행되었으나 아무것도 리턴되지 않음을 의미한다. 대표적인 예로 DELETE가 있다. API DELETE /companies/43/employees/2 는 employee 2를 삭제하고 리턴되는 API response body에는 아무런 데이터도 필요하지 않다. 이는 시스템에 명시적으로 삭제하도록 요청했기 때문이다. 만약 employee 2가 데이터베이스에 존재하지 않는 것과 같은 에러가 발생한다면, 2xx Success Category 가 아닌, 4xx Client Error category response code를 리턴하게 된다.

3xx (Redirection Category)

  • 304 Not Modified 는 client가 해당 response를 이미 캐시로 갖고 있음을 의미한다. 따라서 같은 데이터를 다시 전달할 필요가 없다.

4xx (Client Error Category)

아래의 status code들은 client가 잘못된 request를 전달했음을 의미한다.

  • 400 Bad Request 는 client의 요청사항을 서버가 이해하지 못해서 request가 정상적으로 수행되지 않았음을 의미한다.
  • 401 Unauthorized 는 client가 resources에 대한 접근 권한이 없으며 필요한 자격을 갖추고 다시 request 해야 함을 의미한다.
  • 403 Forbidden 는 request가 유효하며, client의 권한에도 문제가 없지만 어떠한 이유로 인해서 client가 resource 페이지에 접근할 수 없음을 의미한다. 예를 들어 때때로 인증된 client가 서버의 디렉토리에 접근할 수 없는 경우가 있다.
  • 404 Not Found 는 요청된 resource가 현재 사용 불가능 함을 의미한다.
  • 410 Gone 는 요청된 resource가 의도적으로 이동되어 더 이상 사용 불가능 함을 의미한다.

5xx (Server Error Category)

  • 500 Internal Server Error 는 request가 유효하지만, 서버에 문제가 발생하여 예상치 못한 조건을 제공하도록 요청받았음을 의미한다.
  • 503 Service Unavailable 는 서버가 다운되거나 request를 처리할 수 없음을 의미한다. 대부분 서버 점검시에 발생한다.

5) 필드 네이밍 컨벤션

원하는 네이밍 컨벤션을 사용할 수 있지만, 어플리케이션 전체에 걸쳐서 일관성을 갖는 것이 중요하다. 만약 request body 혹은 response type이 JSON이라면 일관성을 위해서 카멜케이스 규칙을 따르는 것이 좋다.

6) 검색, 정렬, 필터링, 페이지네이션

이러한 모든 작업은 하나의 데이터 집합에 대한 쿼리일 뿐이다. 이 같은 action을 처리하기 위한 별도의 API set은 존재하지 않는다. 다만 API의 GET 메소드에 쿼리 파라미터를 추가 할 필요가 있다. 몇가지 예시를 통해서 해당 action 들을 어떻게 구현할 수 있는지 살펴보자.

  • Sorting 정렬된 companies 리스트를 원하는 경우, GET /companies endpoint는 여러개의 정렬 매개변수를 쿼리에서 받아야 한다. 예를 들어 GET /companies?sort=rank_asc 는 rank 오름차순으로 companies를 정렬해야 한다.
  • Filtering 데이터셋 필터링을 위해서 쿼리 매개변수를 통해 다양한 조건을 전달할 수 있다. 예를 들어 GET /companies?category=banking&location=india 는 india에 위치하는 banking 카테고리의 companies를 필터링해야 한다.
  • Searching companies list 중에서 이름을 검색하려면 API endpoint는 GET /companies?search=Digital Mckinsey 와 같아야 한다.
  • Pagination 데이터셋이 너무 큰 경우, 데이터를 작은 덩어리로 나눔으로써 퍼포먼스를 향상시키고 response 핸들링을 좀 더 편하게 할 수 있다. 예를 들어 GET /companies?page=23 은 companies 리스트의 23번째 페이지를 의미한다.

만약 GET 메소드에 많은 쿼리 매개변수를 전달하여 URI가 너무 길어진다면 서버는 414 URI Too long HTTP status를 응답할 것이다. 이런 경우에는 POST 메소드의 request body를 통해서도 매개변수를 전달할 수 있다.

7) Versioning

당신의 API가 여러 곳에서 사용되고 있을 때 일부 변경 사항으로 API를 업그레이드하면, 기존의 API를 사용하는 제품이나 서비스에 문제가 발생할 수 있다.

http://api.yourservice.com/v1/companies/34/employees 처럼 경로에 API의 버전을 포함하는 것은 좋은 예시이다. 만약 주요 업데이트가 있는 경우에는 v2, v1.x.x 와 같은 새로운 API set을 위한 이름을 사용할 수 있다.



https://medium.com/@hyunalee419/javascript-%ED%91%9C%EC%A4%80-58799e7a922f



JavaScript 표준

이현아
이현아
Sep 13, 2018 · 13 min read

EMCAScript

Ecma International

종류

ES1, ES2, ES3, ES4

ES5

ES6 / ES2015

ES7 / ES2016

let numbers = [1, 2, 3, 4];if (numbers.indexOf(2) !== -1) {console.log('Contains');}// ES2016if (numbers.includes(2)) {console.log('Contains');}
// NaN 포함여부 확인let numbers = [1, 2, 3, 4, NaN];console.log(numbers.indexOf(NaN)); // prints -1console.log(numbers.includes(NaN)); // prints true
let base = 3;let exponent = 4;console.log(base**exponent); // 81

ES8 / ES2017

ES9 / ES2018


TypeScript

TypeScript란?

TypeScript의 특징

TypeScript와 ECMAScript

TypeScript — The Myth of the Superset

왜 TypeScript를 사용해야하는가?

TypeScript의 구성요소



https://weicomes.tistory.com/63



[참고]



직렬화란?


직렬화를 왜 하냐고 물어본다면 다른 환경과 데이터를 주고 받으려면 동일한 데이터 구조를 가져야 하기 때문이다. 다른 환경이란 여러 주체들이 될 수 있다. 프로그램, 서버, 다른 컴퓨터, 핸드폰, TV 등 다양한 디바이스
제각기 다른 언어를 사용한다면 데이터 송신이 이루어질 수 없다.따라서, 파이썬만 직렬화를 하는 것이 아니다. C, Java, PHP, R 여러 언어들이 직렬화를 지원한다.

시리얼라이즈는 마치 줄줄이 소시지처럼 통일된 데이터를 전송가능한 형태로(데이터 스트림) 만드는 것이라고 생각하면 된다.


일련의 바이트로부터 데이터 구조를 추출하는 것이 반직렬화(deserialization)한다.

객체의 직렬화는  Object 또는 Data를  외부의 시스템에서도 사용할 수 있도록 바이트 단위로 변환하여 파일 또는 네트워크를 통해서 스트림(송수신)이 가능하도록 하는 것을 의미한다.


자바의 I/O 처리는, 정수, 문자열 바이트 단위의 처리만 지원했었다. 따라서 복잡한 내용을 저장/복원 하거나, 네트워크로 전송하기 위해서는 객체의 멤버변수의 각 내용을 일정한 형식으로 만들어(이것을 패킷이라고 한다) 전송해야 했다.


객체직렬화는 객체의 내용(구체적으로는 멤버변수의 내용)을 자바 I/O가 자동적으로 바이트 단위로 변환하여, 저장/복원하거나 네트워크로 전송할 수 있도록 기능을 제공해준다. 즉 개발자 입장에서는 객체가 아무리 복잡하더라도, 객체직렬화를 이용하면 객체의 내용을 자바 I/O가 자동으로 바이트 단위로 변환하여 저장이나 전송을 해주게 된다. 


자바에서 직렬화는 자동으로 처리해주는 것이기 때문에, 운영체제가 달라도 전혀 문제되지 않는다. 객체를 직렬화할때 객체의 맴버변수가 다른 객체(Serializable 인터페이스를 구현한)의 레퍼런스 변수인 경우에는 레퍼런스 변수가 가리키는 해당 객체까지도 같이 객체직렬화를 해버린다.


또 그 객체가 다른 객체를 다시 가리키고 있다면, 같은 식으로 색체 직렬화가 계속해서 일어나게 된다. 이것은 마치 객체직렬화를 처음 시작한 객체를 중심으로 트리 구조의 객체직렬화가 연속적으로 일어나는 것이다. 




직렬화의 장점


객체 내용을 입출력형식에 구애받지 않고 객체를 파일에 저장함으로써 영속성을 제공한다.

객체를 네트워크를 통해 손쉽게 교환할 수 있다.




객체 전송의 단계 


객체를 분해하여 전송하기 위해서는 직렬화(Serialization) 되어야 한다.


객체를 전송하기 위해서는 3가지 단계를 거친다.


(1) 직렬화된 객체를 바이트 단위로 분해한다. (marshalling)

(2) 직렬화 되어 분해된 데이터를 순서에 따라 전송한다.

(3) 전송 받은 데이터를 원래대로 복구한다. (unmarshalling) 



Marshalling


- 마샬링(Marshalling)은 데이터를 바이트의 덩어리로 만들어 스트림에 보낼 수 있는 형태로 바꾸는 변환 작업을 뜻한다. 


- 자바에서 마샬링을 적용할 수 있는 데이터는 원시 자료형과 객체 중에서 Serializable 인터페이스를 구현한 클래스로 만들어진 객체이다.


- 객체는 원시 자료형과 달리 일정한 크기를 가지지 않고 객체 내부의 멤버 변수가 다르기 때문에 크기가 천차만별로 달라진다. 이런 문제점을 처리할 수 있는게 ObjectOutputStream 클래스이다. 




직렬화 


- 마샬링으로 바이트로 분해된 객체는 스트림을 통해 전송될 준비가 되었다. 앞에서 언급한대로 객체를 마샬링하기 위해서는 Serializable 인터페이스를 구현한 클래스로 만들어진 객체에 한해서만 마샬링이 진행될 수 있다.

Serializable 인터페이스는 아무런 메소드가 없고 단순히 자바 버추얼 머신에게 정보를 전달하는 의미만을 가진다.


*직렬화가 가능한 객체의 조건

(1) 기본형 타입(boolean, char, … )은 직렬화가 가능

(2) Serializable 인터페이스를 구현한 객체여야 한다.

(3) 해당 객체의 멤버들 중에 Serializable 인터페이스가 구현되지 않은 객체가 존재해서는 안된다.

(4) transient가 사용된 멤버는 전송되지 않는다. (보안 변수 : null 전송)



Unmarshalling


언마샬링은 객체 스트림을 통해서 전달된 바이트 덩어리를 원래의 객체로 복구하는 작업이다. 이 작업을 제대로 수행하기 위해서는 반드시 어떤 객체 형태로 복구할지 형 변환을 정확하게 해주어야 한다. 




SerialVersionUID, 클래스의 버전관리


직렬화하면 내부에서 자동으로 SerialVersionUID라는 고유의 번호를 생성하여 관리한다. 이 UID는 직렬화와 역직렬화 할 때 중요한 역할을 한다. 이 값이 맞는지 확인 후 처리하기 때문이다. 만약 이 SerialVersionUID 값이 맞지 않는다면 오류를 출력한다.


객체 생성 당시의 UID와 현재 변경한 이후의 UID는 내부에서 자동으로 생성되어 관리되므로 UID가 달라 오류가 발생할 수 있다. 이러한 문제로 Java에서는 SerialVersionUID를 직접 선언하고 관리하는 방식을 적극 권장하고 있다. 


https://hamait.tistory.com/880



파이썬 로깅의 모든것

[하마] 이승현 (wowlsh93@gmail.com) 2017.08.07 11:09


파이썬 로깅의 모든것 

파이썬 로깅에 대한 "모든것은" 사실 낚시구요. ㅎㅎ (유희열이 진행했던 All that music 이라는 라디오 프로그램에서 내가 좋아하는 국악이나 시부야계는 거의 나오지도 않았었다...먼소리야 이게 ;;) 그래도 블로그용으로는 꽤나 많은 정보를 담으려고 애를 썼습니다. 파이썬에서 자체 제공하는 기본 로깅 시스템에 대한 내용이구요. 버전이 올라감에 따라 내용이 달라질수 있으며, 오류도 있을 것이고 더 손쉬운 방법등이 있을 수 있음을 인지하시고 항상 다른것들도 같이 확인 하세요. 

본론으로 바로 들어가시죠. 

먼저 제가 원하는 로깅 시스템은 이렇습니다.  IoT 의 게이트웨이 안에서 활약 할 놈으로써~

  1. logging.conf 파일을 통해서 외부에서 설정 가능

  2. 레벨을 자유롭게 설정하고 추가 할 수 있어야 한다.
    (CRITICAL, ERROR,INFO, DEBUG )

  3. 파일,스트림에 동시에 출력 할 수 있어야 한다.

  4. 다양한 목적에 따라 다양한 파일에 출력 할 수 있어야 한다.

  5. 로깅 시간 출력 및 다양한 정보에 대한 추가가 가능해야 한다.

  6. 하루에 한번씩 파일을 생성 해야하며, 지난 파일은 압축하여 보관 해야한다.

  7. 하루에 한번씩 파일을 생성 해야하며, 오래된 파일을 삭제 할 수 있어야 한다.

  8. 파일 용량이 너무 커질 경우에는 자동으로 분리 시켜야 한다.

  9. 멀티프로세스를 활용해야 하는 파이썬의 특성상 멀티프로세스에서도 로그를 취득할 수 있어야 한다.

  10. 핸들러를 커스터마이징 할 수 있어야 한다. 즉 콘솔,파일 출력 뿐 만 아니라, DB 및 소켓을 통하여도 발송 할 수 있어야 한다.

  11. 사용하기 쉬워야 한다. (문서화가 잘 되 있어야 한다) , 기본 로깅 모듈을 사용해야한다.

이것을 해결 하기 위해 알아야 할 것들에 대해 공부해 보는것이 이 글의 목표 입니다. 즉 이 글을 모두 읽고 나면 모든 기능을 본인의 프로젝트에서 사용 할 수 있게 될 것이라 봅니다.

* 참고로 자바개발자에게 익숙한 log4j 같은 거대한 로깅 시스템에 비하면 제공되는 기능이 조금 부족합니다. 몇가지는 직접만들어야합니다.
* 2.x / 3.x 상관없으나, 몇몇 부분은 2.7 기준입니다. 3.x 버전의 새기능 활용 안함. 

초급 

로깅이란? 

현재 우리의 프로그램이 어떤 상태를 가지고 있는지, 외부출력을 하게 만들어서 개발자등이 눈으로 직접 확인하는 것 입니다. 이때 

print (" 난 지금 매우 좋아 ") 
print (" 30이 들어왔어")

이렇게 콘솔로 출력하는 방법이 가장 쉽고 편하긴 합니다만, 파일로의 출력 등 좀 더 다양한 기능을 사용하고 싶을 때 우리는 로깅 시스템을 따로 만들거나, 기존에 구축되어진 라이브러리를 가져와서 사용합니다. 

로깅 모듈 사용하기

파이썬에서 로깅 모듈은 기본 라이브러리에 포함되어 있기 때문에, 굳이 따로 설치 할 필요는 없습니다.

import logging

if __name__ == '__main__':
logging.error("something wrong!!") 결과: ERROR:root:somthing wrong!!

이렇게 logging 모듈을 임포트해서 그냥 사용하면 됩니다. 
위에서는 아무 전처리도 하지 않고 그저  logging 모듈 안의 error 함수를 호출 했습니다. 

로깅 레벨

위에서는 출력 할 때 error 함수를 사용했는데요. 이름에서 느껴지다 시피 먼가 큰 문제가 생겼음을 알려주기 위한 출력이었습니다. 하지만 평범한 정보를 알려 주기 위해서 사용하고 싶을 때가 있을 것입니다.

" 지금 서버가 시작됬어요."
" 서버의 대기포트는 이 번호로 시작 됬습니다"
" 어떤 함수가 호출 되었어요"
" 100이 들어왔어요"

이럴 때는 info 같은 레벨이 좋을거 같습니다. 즉 출력하는 정보의 등급을 나누어서, 구분 지으면 나중에 확인 할 때 편할 거 같습니다.보통 DEBUG, INFO, WARNING, ERROR, CRITICAL 의 5가지 등급이 기본적으로 사용됩니다.

import logging

if __name__ == '__main__':
logging.info("hello world")

응?  출력이 안되네요? 

여기서 예를 하나 들어보죠. 만약 print 로 모든 로깅을 출력한다면, 실제 프로그램이 배포 되었을 경우, 수많은 정보를 출력하게 되고  그것이 오히려 프로그램에 성능 부담이 갈 수도 있겠죠. 또한 출력을 파일로 한다면 파일이 너무 커져서 시스템을 멈출 수도 있을 거 같습니다. 따라서 설정 같은 것을 통해 로깅을 출력을 하지 말아라~ 라고 설정해서 로깅이 출력이 안되게 하면 매우 편할 거 같습니다. 

보통 DEBUG 레벨에서 굉장히 많은 정보들을 출력하게 되는데, 개발시에만 DEBUG 레벨의 정보를 보고 싶고, 실제 서비스를 할 경우는 DEBUG 레벨은 보고싶지 않을 수 있습니다.

INFO 레벨보다 심각한 것만 출력하게 하라!! 라고 로깅 시스템을 사용하면 설정 가능합니다.

파이썬 로깅의 기본 설정은 WARNING 입니다. 따라서  DEBUG < INFO < WARNING < ERROR < CRITICAL  
이 순서에서 더 높은 레벨인 ERROR 는 출력이 되지만, 하위레벨 (INFO,DEBUG) 은 출력이 안됩니다.
즉 이 레벨을 DEBUG 나 INFO 로 낮추어 설정 한후에 사용해야 합니다. 설정하는 방법은 조금 있다가 살펴보죠.


나의 로깅 모듈 사용하기 - 1 (생성)
제일 처음에  logging.error("somthing wrong") 이렇게 error 함수를 바로 사용 했었는데요.
근데 이렇게 직접 함수를 호출하기도 하지만, 자신만의 특정한 로거를 따로 만들어서 사용하는게 보통입니다.

import logging

if __name__ == '__main__':
mylogger = logging.getLogger("my")

이렇게 호출하면 "my" 라는 특정 로거를 생성하게 됩니다.

나의 로깅 모듈 사용하기 - 2 (레벨 설정)

import logging

if __name__ == '__main__':
mylogger = logging.getLogger("my")
mylogger.setLevel(logging.INFO)

setLevel 메소드를 통해서 INFO 레벨 이상은 출력 하도록 설정 하였습니다. (DEBUG 출력 안함)

나의 로깅 모듈 사용하기 - 3 (핸들러 설정)

import logging

if __name__ == '__main__':
mylogger = logging.getLogger("my")
mylogger.setLevel(logging.INFO)

stream_hander = logging.StreamHandler()
mylogger.addHandler(stream_hander)

mylogger.info("server start!!!") 결과: server start!!!

핸들러란 내가 로깅한 정보가 출력되는 위치 설정하는 것을 말합니다. 위에서는 콘솔을 통해 출력하도록 설정 했지만, 파일,DB,소켓,큐등을 통해 출력 하도록 설정 할 수도 있습니다.

나의 로깅 모듈 사용하기 - 4 (파일 핸들러 설정)

파일로도 동시에 출력하게 해봅시다. 매우 간단합니다.

#!/usr/bin/python

import logging

if __name__ == '__main__':
mylogger = logging.getLogger("my")
mylogger.setLevel(logging.INFO)

stream_hander = logging.StreamHandler()
mylogger.addHandler(stream_hander)

file_handler = logging.FileHandler('my.log')
mylogger.addHandler(file_handler)

mylogger.info("server start!!!")

logging.FileHandler 클래스를 통해 객체를 만들어서 나의 로거에 추가해주면 됩니다. 현재 디렉토리에 파일(my.log)이 만들어 질 것 입니다.

노트:  파일은 a 모드로 열리는게 디폴트입니다. a 모드란 같은 이름의 파일이 이미 있다면, 파일 끝에 추가하여 쓰고, 없다면 쓰기용으로 파일 만들라는 뜻입니다.

나의 로깅 모듈 사용하기 - 5 (출력 포매팅 설정)

server start!!! 라는 메세지 말고도, 이 메세지가 언제 쓰여졌는지, 어떤 모듈에서 쓰여졌는지 등 기타 정보를 같이 출력하고 싶을 것입니다. 이때 포매팅이란 것을 하게 되는데요.

import logging

if __name__ == '__main__':
mylogger = logging.getLogger("my")
mylogger.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

stream_hander = logging.StreamHandler()
stream_hander.setFormatter(formatter)
mylogger.addHandler(stream_hander)

file_handler = logging.FileHandler('my.log')
mylogger.addHandler(file_handler)

mylogger.info("server start!!!") 결과: 2017-08-07 12:00:29,141 - my - INFO - server start!!!

포매팅에 추가한 정보를 모든 로그 데이터에 추가해서 출력하게 됩니다.  

asctime 시간
name 로거이름
levelname 로깅레벨 
message 메세지 

이것 말고도 모듈이름, 파일이름, 출력라인넘버등 다앙하게 많으니, 더 많은 정보는 메뉴얼을 참고 하시구요. 위에서는 포매터를 콘솔출력에 한정해서 적용했기에 파일은 기존 그대로 출력 될 것입니다. 파일도 포매팅되게 하고 싶으면 역시 포매터를 파일 핸들러에도 추가하세요.

중급 

이제 중급부터는 좀 더 인사이드에 대한 고찰이 시작됩니다. 사실 파이썬이라는 언어는 읽고 이해하기 간결한 언어이기 때문에 실제 로깅 소스 모듈을 직접 읽어 보시는게 가장 명쾌하게 이해 할 수 있을 것입니다. 저도 내용 검증은 직접 소스를 확인 하였으며 참고로 파이참을 쓰실 경우 컨트롤 + 좌클릭으로 손쉽게 소스를 확인 할 수 있습니다. 

로깅 모듈 생성에 관한 고찰 

자 위에서 나만의 로깅 객체를 만들 때 logging.getLogger("my") 라고 만들었었는데요.  
그냥 매개변수 없이 logging.getLogger() or logging.getLogger("") 이렇게 만들 수도 있습니다. 

이렇게 하면 루트로거 를 리턴하게 되며, 루트로거는 로깅시스템의 기본로거이고 기본레벨은 warning 을 가지고 있습니다. 우리가 젤 처음에 로거를 만들지 않고 그냥 logging.error("...") 식으로 사용했었죠? 내부에서는 루트로거를 사용하고 있답니다.

로깅 생성에 대한 특성을 정리 해 보면 아래와 같습니다. 

* logging.getLogger() 를 통해 루트 로거 얻어서 사용 할수 있습니다.(기본레벨은 warning)
* logging.error() 를 직접 호출하면 내부에서 루트로거를 이용합니다.
* Logger 인스턴스는 이름으로 식별됩니다.
* 동일 모듈 뿐만 아니라, 여러 모듈에서 동일한 인스턴스이름 logging.getLogger('someLogger')를 여러 번 호출해도 동일한 로거 객체에 대한 참조가 반환됩니다.
* 빈 문자열인 "" 라는 이름의 루트 로거가 있으며, 다른 Logger 들은 모두 루트 Logger 의 자식들입니다. 따라서 루트 로거에 기본적인 설정을 해두고 사용하면 자식들은 모두 동일한 설정을 사용하게 됩니다.(핸들러,포매터등) 
*이름은 마침표(.) 로 구분되는 문자열이며 계층 구조를 형성한다. 즉 a 라는 로거를 만들고, a.b 라는 로거를 만들면 a.b는 a 로거의 자식입니다. 
하나의 모듈에서 상위 로거를 정의 및 구성하고 별도의 모듈에서 하위 로거를 생성 할 수 있으며 하위에 대한 모든 로거 호출은 상위 노드로 전달됩니다.
* 가능하면 루트 로거를 사용하기 보다는 각 클래스나 모듈마다 별도의 로거를 만드는 것을 추천합니다.
* 로깅 메세지는 부가정보가 합쳐져서 LogRecords 로 생성되며, 이 객체를 가지고 각각의 handler 는 파일,콘솔,TCP 등으로 로 emit (출력) 합니다

로깅 모듈 생성 실제 - 로거 범위 

보통 main 에서 로깅 시스템 (대표설정) 을 만들어서 사용하게 됩니다. 이것을 디폴트로 자식 로거들에서 사용하게 되며, 자식별로 필요하면 특정 설정을 하게 됩니다. 또한 핸들러는 버퍼링을 하고 있기 때문에 어플리케이션이 갑자기 중지되기 전에 logging.shutdown() 을 호출해서 모두 출력해 주면 좋습니다. (fianllly 절에서 수행) 

이제 로거를 만드는 대표적인 범위를 살펴보시죠.

@ 모듈명 : 많은 함수와 클래스들을 포함하는 모듈의 전역 Logger 인스턴스로 생성 

import logging
logger = logging.getLogger(__name__)

@객체인스턴스 : __init__() 메소드 내에서 Logger 를 생성. 인스턴스 마다 고유하다. 

def __init__(self, name)
self.logger = logging.getLogger("{0}.{1}".format(self.__class__.qualname__, name))

@클래스명 : __class__.__qualname__  만으로 생성 

@함수명 :  잘 사용되지 않는 큰 함수라면 함수 내에서 로그를 생성 할 수도 있다.

def main():
    logger = logging.getLogger("main") 

파이썬에서는 하나의 파일이 하나의 모듈과 일치합니다. 따라서 파일의 제일 위쪽에서 모듈명으로 로거객체를 만들 수 있으며, 함수별/클래스별/객체별로도 만들 수 있습니다. 자신의 로깅 전략에 따라서 만들면 될 거 같습니다.

로깅 모듈 생성 실제 - 로거 부모/자식 관계 

위에서 로거는 부모/자식관계를 갖는다고 말했는데요. 즉 루트로거는 모든 로거의 부모이며,  a.b 라는 로거는 a 라는 로거의 자식입니다. ( a라는 로거가 없는데 a.b 로거를 만들 수도 있습니다. 내부적으로 a 로거는 자동으로 place holding 됩니다) 

저런 관계에 따른 이상 행동을 예측 하기 위해서는 부모/자식간의 행동을 파악할 필요가 있는데요.
다음 코드를 보시죠. 

# 루트 로거 생성 rootlogger = logging.getLogger()
rootlogger.setLevel(logging.INFO)
stream_hander = logging.StreamHandler()
rootlogger.addHandler(stream_hander)
# "my" 로거 생성
mylogger = logging.getLogger("my")
mylogger.setLevel(logging.INFO)
stream_hander2 = logging.StreamHandler()
mylogger.addHandler(stream_hander2)
# "my" 로거 생성
mylogger.info("message from mylogger")

루트로거를 a 모듈에서 만들고, "my" 로거를 다른 곳에서 만들었다고 칩시다.( 위에 소스에서는 보기 쉽게 같은 곳에 두었음)  "my" 로거에서 "message from mylogger" 를 출력 했는데, 결과는 어떻게 될까요? 

message from mylogger
message from mylogger
 
이렇게 2개의 메세지가 출력되는데 상위로 전파되었기 때문입니다.
이것을 막으려면, 간단하게는 루트로거에서 핸들러를 추가해 주지 않아도 되며, 혹은
mylogger.propagate = 0
propagate 를 0 으로 세팅하면 상위로 전파하지 않습니다.

로깅 모듈 생성 실제 - 파일을 통한 설정 

외부 파일을 통해서, 로깅레벨, 핸들러 (파일로 할것인지, 콘솔로 할 것인지 등) 와 포매터등을 설정 할 수도 있습니다. 이렇게 되면 굳이 소스를 건드리지 않고도 간편히 설정가능하겠지요? 
파일 포맷은 JSON, INI, YAML 등 다양하게 할 수 있습니다만 여기서는 JSON을 살펴보겠습니다.

logging.json 파일은 아래와 같습니다.

{
"version": 1,
"formatters": {
"simple": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
},

"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "simple",
"stream": "ext://sys.stdout"
},

"info_file_handler": {
"class": "logging.FileHandler",
"level": "DEBUG",
"formatter": "simple",
"filename": "info.log"
}
},

"root": {
"level": "DEBUG",
"handlers": ["console", "info_file_handler"]
}
}

아래는 실제 프로젝트에서 사용한 logging config
: dev 환경과 pro환경에서 다르게 작동하도록 config를 나누었다.
{
"dev":{
'version':1,
'formatters':{
'error':{
'format':ERROR_FORMAT
},
'else':{
'format':ELSE_FORMAT
}
},
'handlers':{
'console':{
'class':'logging.StreamHandler',
'formatter':'else',
'level':'DEBUG'
},
'file_else': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'DEBUG',
'formatter': 'else',
'filename': './log/app.log',
'mode': 'a',
'maxBytes': 10485760,
'backupCount': 5,
},
'file_error': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'ERROR',
'formatter': 'error',
'filename': './log/error.log',
'mode': 'a',
'maxBytes': 10485760,
'backupCount': 5,
}
},
'root':{
'handlers':('console','file_else','file_error'),
'level':'DEBUG'
}
},
"pro":{
'version':1,
'formatters':{
'error':{
'format':ERROR_FORMAT
},
'else':{
'format':ELSE_FORMAT
}
},
'handlers':{
'console':{
'class':'logging.StreamHandler',
'formatter':'else',
'level':'DEBUG'
},
'file_else': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'INFO',
'formatter': 'else',
'filename': './log/app.log',
'mode': 'a',
'maxBytes': 10485760,
'backupCount': 5,
},
'file_error': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'ERROR',
'formatter': 'error',
'filename': './log/error.log',
'mode': 'a',
'maxBytes': 10485760,
'backupCount': 5,
}
},
'root':{
'handlers':('console','file_else','file_error'),
'level':'DEBUG'
}
},


포매터를 작성했으며, 핸들러를 두개 만들었습니다. root 로거에 대한 설정도 했네요.
파일핸들러를 통해서 info.log 파일에 DEBUG 레벨 이상일 경우 출력합니다. 저것을 실제 서비스시 INFO로 바꾸면 디버깅 출력은 disable 되겠죠. 

루트 로거의 레벨이 DEBUG 이기 때문에 모든 레벨을 다 출력하게 됩니다만, 자식로거에서 레벨을 높히게 되면 자식이 우선순위를 갖게 됩니다.

소스에서는 아래와 같이 logging.json 을 가져다가 사용합니다.


import logging
import logging.config
import json
import os

if __name__ == '__main__':

with open('logging.json', 'rt') as f:
config = json.load(f)

logging.config.dictConfig(config)

logger = logging.getLogger()
logger.info("test!!!")

파일을 읽어서 logging.config.dictConfig 를 통해서 설정을 세팅하고, 루트로거를 가져다가 사용하게 됩니다.
만약 설정 파일에 아래와 같은 특정 로거를 세팅해 주게되면 

"loggers": {
"my_module": {
"level": "ERROR",
"handlers": ["console"],
"propagate": "no"
}
},

logging.getLogger("my_module") 식으로 가져와서 사용하면 저 속성을 갖게 됩니다.

로깅 모듈 생성 실제 - 부가정보 출력하기
로깅 호출에 전달 된 매개 변수 외에도 컨텍스트 정보를 포함하도록 로깅 출력을 원하는 경우가 있습니다.
이때 LoggerAdapters 를 사용 할 수 있는데요.

class LoggerAdapter(logging.LoggerAdapter):
def __init__(self, prefix, logger):
super(LoggerAdapter, self).__init__(logger, {})
self.prefix = prefix

def process(self, msg, kwargs):
return '[%s] %s' % (self.prefix, msg), kwargs

if __name__ == '__main__':

with open('logging.json', 'rt') as f:
config = json.load(f)

logging.config.dictConfig(config)

logger = logging.getLogger("")

logger = LoggerAdapter("SAS", logger)
logger.info("test!!!") 결과: 2017-08-07 12:54:55,911 - root - INFO - [SAS] test!!!!

logging.LoggerAdapter 를 상속받아서 만든 어댑터를 통해서 SAS 라는 문구를 메세지 앞에 고정 시킬 수 있게 됩니다. process 메소드에 prefix 를 추가한것을 확인하세요. 

로깅 모듈 생성 실제 - 커스텀 로그레벨 설정하기 
저같이 센서로 부터 데이터를 받는 IoT 서비스에서 로깅을 출력 할 때 DATA 만을 확인해 보고 싶을 경우가 있습니다. 이 경우 DEBUG 보다 한단계 아래에 로깅 설정을 할 수 있게 되는데요 
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
위처럼 각 레벨에는 숫자가 10단위로 적용되어 있습니다. 예상 할 수 있다시피, 내부에서는

if level > 세팅된 레벨:
   출력 

식으로 되어 있어서 세팅된 레벨보다 출력레벨이 클 경우에 한 해서만 출력하고 있습니다.

if ERROR  > INFO :   세팅이 INFO 면 ERROR 는 출력
if DEBUG > INFO :    세팅이 INFO 면 DEBUG 는 출력하지 않음 

따라서 DATA = 15 이렇게 설정해 두면, DEBUG 정보는 출력하지 않고, DATA 레벨만  출력하게 됩니다.

사용예는 아래와 같습니다. 


logging.addLevelName(15, "DATA")
logging.DATA = 15

logger.log(logging.DATA, "message")


멀티쓰레드에서의 로깅 
스레드에서 로깅하는 것에 대해서도 특별한 노력이 필요하지는 않습니다. 각각의 쓰레드에서 동일한 이름의 로거를 가져 다 사용 해도 됩니다. 포매팅을 통해 쓰레드별로 출력 할 수 있습니다.

다음 예제는 기본 스레드와 멀티 스레드에서 로깅을 보여줍니다. 둘이 같은 루터 로거를 사용했습니다.
import logging
import threading
import time

def worker(arg):
while not arg['stop']:
logging.debug('Hi from myfunc')
time.sleep(0.5)

def main():
logging.basicConfig(level=logging.DEBUG, format='%(relativeCreated)6d %(threadName)s %(message)s')
info = {'stop': False}
thread = threading.Thread(target=worker, args=(info,))
thread.start()
while True:
try:
logging.debug('Hello from main')
time.sleep(0.75)
except KeyboardInterrupt:
info['stop'] = True
break
thread.join()

if __name__ == '__main__':
main()
실행하면 스크립트는 다음과 같이 출력 합니다.

  1 MainThread Hello from main
     1 Thread-1 Hi from myfunc
   503 Thread-1 Hi from myfunc
   751 MainThread Hello from main
  1003 Thread-1 Hi from myfunc
  1503 MainThread Hello from main
  1503 Thread-1 Hi from myfunc
  2004 Thread-1 Hi from myfunc


블럭되는 핸들러 유연하게 다루기 
파이썬 로깅 시스템에서 핸들러를 사용 하여 출력을 하게 되는데, 중간에 버퍼링을 하고 있지 않기 때문에, 쓰기에서 오랜 지연시간이 걸리는 행동을 하게 된다면,  전체 시스템은 느려지게 마련입니다. 주로 이메일 전송의 경우 이런 행동이 자주 나타나곤 하는데, 이 경우 중간 큐를 사용하여 버퍼링을 해 줄 필요가 있습니다. 
 
이때 QueueHandler 와 QueueListener 를 사용하면 됩니다. 문제는 파이썬 2.7에서는 이것을 지원하지 않기 때문에 직접 비스무리하게 만들어야 합니다. 


import Queue
import threading
import traceback
import logging
import sys

class QueueListener(threading.Thread):

def __init__(self, queue, stream_h):
threading.Thread.__init__(self)
self.queue = queue
self.daemon = True
       self.logger = logging.getLogger("main")
self.logger.addHandler(self.handler)
def run(self):
while True:
try:
record = self.queue.get()
self.logger.callHandlers(record)
except (KeyboardInterrupt, SystemExit):
raise
except EOFError:
break
except:
traceback.print_exc(file=sys.stderr)



class QueueHandler(logging.Handler):

def __init__(self, queue):
logging.Handler.__init__(self)
self.queue = queue

def emit(self, record):
self.queue.put(record)


if __name__ == '__main__': # 리스터 부분
logging_q = Queue.Queue(-1)
stream_h = logging.StreamHandler()
log_queue_reader = QueueListener(logging_q,stream_h)
log_queue_reader.start()
# 핸들러 부분
handler = QueueHandler(logging_q)
root = logging.getLogger()
root.addHandler(handler)
     # 사용
root.error("queue handler test!!")

어떻게 활용하는지 설명드리면 

큐 생성)
먼저 버퍼로 사용할 큐를 -1 을 매개변수로 만들고, 

데이터 입력)
큐에 데이터를 담아줄 QueueHandler 를 만듭니다. 핸들러는 기본적으로 emit 메소드를 가지고 있어서 이 메소드를 통해서 log 정보를 (LogRecord 객체) 콘솔로 쏴주거나, 파일로 쏘게 됩니다만, 여기서는 큐에 데이터를 넣습니다.

데이터 처리 ) 
큐에서 데이터 (LogRecord 객체) 를 가져와서, 핸들러에게 넘겨주는 역할을 하는 QueueListener 를 만듭니다.
실제 처리를 담당할 핸들러를 위에서는 스트림 핸들러를 만들어서 매개변수로 넣어주었습니다.

그 후에는 그냥 로거 사용하듯이 사용하면 됩니다. 그러면 중간에 큐를 거쳐서 출력하게 되겠지요. 
위에서는 쓰레드를 사용했지만, 멀티프로세싱을 통해서 할 수도 있으며, 이 방식은 파이썬에서 문제가 되는 멀티프로세싱 로깅을 해결 할 수 있는 실마리를 제공해줍니다. 
이 부분은 고급에서 배우게 될 것입니다.

노트: 
고급을 진행하기 전에 멀티프로세스에 관해서 알아보자. 아시다시피 파이썬에서는 멀티쓰레드가 I/O 를 제외하고 동시계산하는 곳에서는 그닥 효율적이지 않기 때문에, 멀티프로세스를 사용하게 된다. 이때 로깅도 프로세스 각각에서 사용하게 되는데, 이때 동일한 파일이 두 프로세스에 의해 열려져 있기 도 한다.이런 상황에서 여러 문제가 발생하고 해결 해야 하는 문제가 있다.
이전 로그파일을 압축하려고 할 때 우리는 , 압축 할 파일을 잠시 close 하고  압축을 하게 되는데, 윈도우즈에서는 압축이 안되는 현상 및 기존 로그 파일을 날짜가 붙은 새로운 이름으로 변경 하러 할 때도 안되는 현상이 있다.이것은 다른 프로세스에서 그 로그파일을 열고 있기 때문인데 우분투에서는 잘되었다. 아마 파일 디스크립터는 다르기 때문으로 추정한다.  아무튼 운영체제별 이런 차이들이 있어서 좀 골치 아프다. 마지막으로 멀티프로세스 환경의 로깅에서는 
각각 따로 쓰면 파일에 써지지 않기 때문에  멀티프로세스용 큐를 이용하여 하나로 모아서 파일에 쓰게 되는 것을 보게 될 것이다


윈도우) 
- 2개의 프로세스에서 하나의 파일에 쓰기를 할 경우 윈도우는 예측 불가능이고 (하나의 프로세스 것만 써질 수도 있고, 부분 누락이 되며 순서대로 써질 수도 있고)
- 파일 삭제는 불가능
- 압축파일에 파일 옮겨 쓰는것은 예측 불가하다.
- 다만 하나의 프로세스에서 파일을 close 하면 ,그 이후로 다른프로세스의 데이터는 파일에 써지며, close 한 프로세스에서 쓴 데이터의 일부는 압축도 된다.   

리눅스) 
- 2개의 프로세스에서 하나의 파일에 쓰기를 할 경우 리눅스는 하나의 프로세스 것만 써진다.  
- 파일 삭제는 가능하다.
- 압축파일로 옮겨 쓰는 것도 가능하다. 다만 파일을 close 해주고 해야한다. 물론 메인프로세스에서 쓴 내용만 압축된다.


고급

고급은 아래와 같은 주제를 다룹니다.  조금 숨 좀 돌리고 작성 하도록 할께요~다음 기회에 봐요 ~:-) 

* 추가 컨텍스트 정보 출력 하기 / 필터 사용하기 
* 소켓핸들러 작성 
* 멀티프로세싱을 위한 로깅 작성
* 용량 넘어선 로깅파일 나누기 
* 어제로깅파일을 압축하기
* 한달전 로깅파일을 삭제하기  


참고로 기본 로깅보다 조금 더 구색을 갖춘 pylogging 이란 것도 있습니다.
https://github.com/Clivern/pylogging

그래도 먼가 많은 기능들이 다 있으며, 표준처럼 활용하는 멋진 로깅라이브러리가 없는건 아쉽네요.



출처: https://hamait.tistory.com/880 [HAMA 블로그]

https://stackoverflow.com/questions/40001892/reading-named-command-arguments


33

Can I use argparse to read named command line arguments that do not need to be in a specific order? I browsed through the documentation but most of it focused on displaying content based on the arguments provided (such as --h).

Right now, my script reads ordered, unnamed arguments:

myscript.py foo-val bar-val

using sys.argv:

foo = sys.argv[1]
bar = sys.argv[2]

But I would like to change the input so that it is order agnostic by naming arguments:

myscript.py --bar=bar-val --foo=foo-val

42

You can use the Optional Arguments like so:

import argparse, sys

parser=argparse.ArgumentParser()

parser.add_argument('--bar', help='Do the bar option')
parser.add_argument('--foo', help='Foo the program')

args=parser.parse_args()

print args
print sys

Then if you call it with ./prog --bar=bar-val --foo foo-val it prints:

Namespace(bar='bar-val', foo='foo-val')
['Untitled 14.py', '--bar=bar-val', '--foo', 'foo-val']

Or, if the user wants help argparse builds that too:

 $ ./prog -h
usage: Untitled 14.py [-h] [--bar BAR] [--foo FOO]

optional arguments:
  -h, --help  show this help message and exit
  --bar BAR   Do the bar option
  --foo FOO   Foo the program
  • I did not know that it was possible to use the --opt=val syntax. This is wonderful :) – Tryph Oct 12 '16 at 15:29
  • 1
    how do I read the value of foo after the args have been loaded ? – amphibient Oct 12 '16 at 15:30
  • is there like args.get('foo') ? – amphibient Oct 12 '16 at 15:30
  • 4
    Use args.bar to read --bar It will return None if not included in the command. You can also use a default instead. – dawg Oct 12 '16 at 15:33 
  • Using python 3.5.1, and when not included it doesn't return None; it gives an error. So as an alternative, d = vars(args) gives a dictionary (maps named arguments to values) – DZack Nov 13 '17 at 18:12 


https://chaseonsoftware.com/most-common-programming-case-types/#javascript-conventions

Most Common Programming Case Types

Published

2018/08/06

Share: Twitter

When working with computers—specifically while programming—you'll inevitably find yourself naming things (one of the two hard things in computer science).

A major factor of being successful in naming is knowing the type of case you want to use so that you can have a consistent convention per project/workspace. If you're writing software, you'll come across at least one of these in a languages specification for how it's written. Some languages (Go, particularly) rely heavily on you knowing the difference between two of them and using them correctly!



What You'll Learn



camelCase

camelCase must (1) start with a lowercase letter and (2) the first letter of every new subsequent word has its first letter capitalized and is compounded with the previous word.

An example of camel case of the variable camel case var is camelCaseVar.



snake_case

snake_case is as simple as replacing all spaces with a "_" and lowercasing all the words. It's possible to snake_case and mix camelCase and PascalCase but imo, that ultimately defeats the purpose.

An example of snake case of the variable snake case var is snake_case_var.



kebab-case

kebab-case is as simple as replacing all spaces with a "-" and lowercasing all the words. It's possible to kebab-case and mix camelCase and PascalCase but that ultimately defeats the purpose.

An example of kebab case of the variable kebab case var is kebab-case-var.



PascalCase

PascalCase has every word starts with an uppercase letter (unlike camelCase in that the first word starts with a lowercase letter).

An example of pascal case of the variable pascal case var is PascalCaseVar.

Note: It's common to see this confused for camel case, but it's a separate case type altogether.



UPPER

UPPER_CASE_SNAKE_CASE is replacing all the spaces with a "_" and converting all the letters to capitals.

an example of upper case snake case of the variable upper case snake case var is UPPER_CASE_SNAKE_CASE_VAR.



Which case type should I use?

Now that you know the various case types, let's tackle an example of my recommended best practice for filenames and when to use each case for Go, JavaScript, Python & Ruby.

What convention should I use when naming files?

Recommendation: always snake case

When naming files, it's important to ask "what's the lowest common denominator?" If you're not opinionated, I've found I've had the most success with snake case because it's the least likely to create a problem across filesystems and keeps filenames readable for "my_awesome_file".

If you're a Mac user or work with Mac users, it's a good practice to always use lowercase. Mac's have an HFS+ filesystem and since HFS+ is not case sensitive, it can read "MyFile" or "myfile" as "myfile".

My predominant argument for this stems from a particularly insidious "bug" I saw when I was running a CI/CD (continuous integration/continuous delivery) cluster. A CI job failed with "file not found: mycomponent.js" during a build for a React project. The developer swore the file was in the project's source, and as I dug through it, I noticed they had an import for "mycomponenet.js" but the file was named "MyComponent.js" (for a React project, where PascalCase is the convention for naming component files). Due to the way HFS+ handles file casing, it happily accepted that "MyComponent.js" was "mycomponent.js" at the time the developer (using a Mac) was writing the code, but ath the time the Unix based CI server was building it, it would fail because it expected exact casing to find the file.



Go Conventions

Go is the language where it's most critical to pay attention to case type conventions. The language decides whether a variable, field or method should be available to a package caller by if the name starts with a capital or lowercase letter.

  • Pascal case is required for exporting fields and methods in Go
  • Camel case is required for internal fields and methods in Go

 

package casetypes

type ExportedStruct {

unexportedField string

}

In the above example, ExportedStruct is available to package callers for casetypes and unexportedField is only available to methods on ExportedStruct.




Javascript Conventions

  • Camel case for variables and methods.
  • Pascal case for types and classes in JavaScript.
  • Upper case snake case for constants.

React Conventions

I write enough React and it's unique enough that it's worth calling out conventions here as a subsection:

  • Pascal case is used for component names and file names in React.



Ruby Conventions

  • Pascal case is used for classes and modules in Ruby.
  • Snake case for symbols, methods and variables.
  • Upper case snake case for constants.



Python Conventions



Other Conventions

  • kebab case in Lisp.
  • kebab case in HTTP URLs (most-common-programming-case-types/).
  • snake case in JSON property keys.



Quick Comparison Table

Case Type

Example

Original Variable as String

some awesome var

Camel Case

someAwesomeVar

Snake Case

some_awesome_var

Kebab Case

some-awesome-var

Pascal Case

SomeAwesomeVar

Upper Case Snake Case

SOME_AWESOME_VAR

Now that you've been introduced to the most common case types, you're prepared to hop into most of the popular languages and know what conventions to keep when you're writing your own code!

 

貼り付け元  <https://chaseonsoftware.com/most-common-programming-case-types/#javascript-conventions>


'C Lang' 카테고리의 다른 글

Agile manifesto(애자일 선언문)  (0) 2020.06.02
Redmine의Wiki 폴더구성(예시)  (0) 2019.09.17
개발 명명 규칙  (0) 2019.06.07
개발 프로그램 일기  (0) 2018.06.19

+ Recent posts