변수(variable)는 데이터의 실제 위치와 매핑되어 있는 상징적인 이름이다. 어떠한 값에 대한 참조를 가지고 있기 때문에, 특정 값을 계속해서 유지시키기 위해 사용할 수 있어 매우 중요하다. 파이썬은,

  • 런타임에 자료형을 검사하는 동적 검사
  • 이미 특정 자료형으로 정의된 변수에 다른 자료형의 값을 할당할 수 있는 동적 타이핑
  • 인터프리터가 자료형을 추론하여 알맞는 타입을 알아서 준비하는 타입 추론 또는 암시적 타입 선언(implicit type declaration)
  • 암시적 형변환(implicit type conversion)이 지원되지 않아 런타임에 타입 오류를 만나면 에러가 발생하는 강타입 언어다.
  • 이 정도가 파이썬 타입 시스템의 큰 틀이다. 이것저것 어려운 말들 많지만, 아래와 같은 코드를 작성할 수 있다는 것이다.

    인터프리터가 타입을 추론해 주기에 타입을 명시하지 않아도 되고, 동적 타이핑이 지원되기에 정수 타입의 값이 할당되어 있던 변수에 다른 타입의 값을 재할당할 수 있게 된다. 같은 동적 타이핑 언어들과 다르게, 변수 선언에 별도의 키워드(var, let 등)도 필요하지 않다. 타입 시스템 이야기는 짧게 여기서 마무리하자.

    타입과 파이썬 빌트인 타입

    컴퓨터는 전기로 작동한다. 이러한 전기 신호의 on을 1에, off를 0에 대응하고 이들을 조합한 일련의 신호는 10110110...처럼 표현할 수 있다. 이러한 숫자 체계를 수학에서는 이진수라고 하고, 이를 간단한 공식에 대입하여 우리가 흔히 이해하는 10진수로 바꿔서 표시(1101 -> 13)할 수 있다. 0과 1로 만들어지는 값은 결국 수(number)일 뿐이지만, 우리가 현실에서 마주칠 수 있는 데이터들은 수 개념에서 한정되지 않는다. 따라서 컴퓨터 과학에서는 이진수의 조합에 기술적인 기준 몇 가지를 더해 정수와 실수, 문자열 등을 구분하도록 하고 있으며, 일반적인 프로그래밍 언어들도 이러한 체계를 따르고 있다. 이들을 자료형(type)이라고 부른다.

    파이썬에서 제공하고 있는(built-in) 자료형은 크게 기본 자료형컨테이너 자료형으로 나뉜다. 기본 자료형은 아래와 같다.

    • 정수(int) : -1, 0, 1 등
    • 소수(float)복소수(complex) : 1.2, -0.8, 9i+1 등
    • 문자열(string) : 'a', 'hello', '' 등
    • boolean : True, False
    • None : 값이 없음을 의미

    다른 프로그래밍 언어들에서도 사용되는 기초적인 타입들이다. 조금 특별한 점은, 다른 언어처럼 소수 타입이 float와 double의 두가지로 분류되어 있지 않다는 것과, 문자(character)와 문자열(string)을 문자열 타입 하나로 표현하고 있다는 것이다. 그리고 이러한 타입의 데이터들을 조합하여 하나의 단위로 묶어서 다루는 컨테이너 타입들이 있다.

    • 리스트(List) : [1, 2, 3, 4]
    • 튜플(Tuple) : (1, 2, 3, 4)
    • 사전(Dictionary) : {'a': 1, 'b': 2}
    • 집합(Set) : {1, 2, 3, 4}

    이들은 논리적으로 데이터 타입인 동시에 자료 구조 ADT의 구현체기도 하다.(list는 append와 pop으로 스택의 push/pop을 구현하고 있는 등..)

    변수

    a라는 변수를 선언하며, 숫자 리터럴을 a에 할당(assign)했다. 어떤 값을 '직접 써서 표현하는 것'리터럴이라고 한다. 수학에서의 등호는 '같음'을 의미하나, 컴퓨터 과학에서의 등호는 '할당'을 의미한다. 그리고 이 할당의 방향은 오른쪽에서 왼쪽이다. 따라서, 등호를 기준으로 우변이 평가되고, 평가된 값이 좌변에 할당된다. 따라서 변수 a는 타입이 추론되어 정수 타입이 되고, 값으로 1을 가지고 있게 된다. 아래의 할당문을 보면 우변 평가, 좌변 할당이라는 순서를 조금 더 쉽게 이해할 수 있다.

    연산자 개념이 등장했지만, 수학적으로 이해하면 된다. 우변인 3 * 5 + 2가 평가되어 17이 되고, 좌변인 a에 할당되어 결론적으로는 a에 17이 할당된다. 우변에 들어간, 값들과 연산자를 함께 사용하여 수식을 표현한 것을 표현식(expression)이라고 한다. 그 자체로 평가되어 하나의 결과값으로 축약되므로, 이를 평가식이라고도 한다. 리터럴로 값을 표현해놓은 것 그 자체도 expression이 될 수 있다. 아무튼 표현식은 쉽게 보면 '간단한 수식'이지만, 언어 구조의 근간을 이루는 매우 중요한 개념이므로 숙지하고 있는 것이 좋다.

    파이썬은 아래처럼 변수에 대해 몇가지 트릭을 사용할 수 있다.

    1번 라인은 동일한 값을 가진 여러 개의 변수를 선언하는 것, 2번 라인은 서로 다른 값을 가진 변수 여러 개를 인라인으로 선언하는 것이다. 2번 라인은 사실 tuple 표현식과 unpack을 응용한 건데, 이건 나중에 알아보도록 하자. 아무튼, 이런 게 가능하다.

    파이썬의 타입 시스템부터, 빌트인 타입과 변수 개념을 알아봤다. 다음 글부터는 자료형 위주의 이야기들을 채울 예정이다.

    '프로그래밍 > Python' 카테고리의 다른 글

    산술 연산자  (0) 2019.01.28
    숫자 자료형과 리터럴  (0) 2019.01.25
    주석  (0) 2019.01.07
    Hello World, 세미콜론은 optional하다?  (0) 2019.01.03
    개요와 설치  (0) 2019.01.01

    Tree도 ADT기에, 여러 방식의 표현법이 존재한다. 트리의 차수나 노드의 수 등에 영향을 끼치지 않는, 모든 경우의 트리를 표현할 수 있는 표현 방식들을 알아보자.

    Linked list 표현

    선형 자료구조인 linked list를 응용하는 방식이다. 단방향/양방향에 관계 없이 연결 리스트는 오직 연결되어 있는 값과의 link를 위해 사용했는데, 여기서는 child node와의 연결을 표현하기 위해 사용한다.

    노드의 삽입과 삭제가 용이하나, 각 노드의 차수만큼 가변적인 포인터 필드를 가져야 한다는 문제가 있다.

    Left-Child Right-Sibling(LCRS) 표현

    Sibling동일한 parent node를 가지고 있는 노드를 의미하며, right sibling바로 오른쪽에 있는 sibling을 의미한다. 따라서 LCRS 표현에서는 각 노드가 데이터|가장 왼쪽 child의 주소|바로 오른쪽 sibling의 주소로 이루어진다. 아래 그림은 tree를 LCRS로 표현하는 예다.

    '컴퓨터 기초 > 자료구조' 카테고리의 다른 글

    Binary Tree의 순회  (0) 2019.02.01
    n-ary Tree와 Complete, Perfect, Balanced, Skewed Tree  (0) 2019.01.30
    Tree  (0) 2019.01.18
    Stack과 Queue  (0) 2019.01.16
    Array와 Dynamic Array, Linked List  (0) 2019.01.11

    대표적인 선형 자료구조가 StackQueue라면, 대표적인 비선형 자료구조로는 TreeGraph가 있다고 이야기할 수 있다. 그래프 이론에서는 tree도 graph에 속하고(회로가 없는 연결 무향 그래프를 tree라고 하기 때문) 자료구조에서도 tree가 그래프의 일종이라고 보는 듯 하다.

    '나무'라는 의미에 맞게, 뿌리와 가지, 잎 개념이 들어가 있는 계층적 구조를 띄고 있다. 주된 목적은 탐색이며, 따라서 검색 엔진, 데이터베이스, 라우터 알고리즘 등 계층적 데이터를 다루는 수많은 곳에서 응용되고 있다. 아래는 tree의 예다.

    현실에서 볼 수 있는 나무와는 다르게 위에서 아래 방향으로 뻗어나오는 모양이다. 따라서 root(뿌리)도 맨 위에 있다. 트리는 기본적으로 NodeEdge로 구성된다. A, B, C처럼 각각의 요소를 node라고 부르고, node들을 이어주는 간선을 edge라고 부른다. Node는 종류에 따라 아래처럼 나뉜다.

    • Root node : tree의 시작점이 되는 노드. 위 그림에서는 A가 root node이며, tree에서 root node는 최대 하나다.
    • Parent node : tree의 부분집합sub-tree라고 쳤을 때(위 그림에서는 D, H, I로 이뤄진 tree), sub-tree에서의 root node를 의미한다. Root node만 유일하게 parent node가 없다.
    • Child node : Parent node에서 뻗어나온 노드를 의미한다. Parent node와 child node는 상대적인 개념이라, 위 그림에서 C는 A의 child node이면서 F와 G의 parent node이다.
    • Leaf node : tree의 끝점이 되는 노드(Child node가 하나도 없는 노드). 단말(terminal) 노드라고도 부른다.
    • Internal node : Leaf node가 아닌 노드를 의미한다.

    Tree에는 해석학의 이론이 존재하기에, 해석에 관련된 몇가지 개념이 들어가 있다. 그들 중 가장 기본적인 게 차수, 높이, 레벨이다.

    • 차수(degree) : 어떤 노드가 가지고 있는 child node의 수를 의미한다. 위 그림에서는 leaf node들을 제외하면 E만 차수가 1이다. leaf node는 child node가 없는 노드를 의미하므로 일반적으로 leaf node에 대해서는 차수를 이야기하지 않는다. Tree에서 발견되는 가장 높은 차수를 tree의 차수라고 말하기도 한다. binary tree, ternary tree 등 n-ary tree의 조건이 되는 개념이다.
    • 높이(height) : Root node부터 시작하여, 가장 깊게 뻗어나가 있는 leaf node까지의 깊이를 말한다. root node는 높이를 0으로 둔다.
    • 레벨(level) : 각 층의 번호를 의미한다. root node의 level은 0이다.

    따라서 위 tree의 차수는 2, 높이는 3이며, root node는 A다. 노드 C는 level 1에 있으며 parent node로 A를, child node로 leaf node인 F와 G를 두고 있는 internal node라고 이야기할 수 있다.

    '컴퓨터 기초 > 자료구조' 카테고리의 다른 글

    n-ary Tree와 Complete, Perfect, Balanced, Skewed Tree  (0) 2019.01.30
    Tree의 표현(representation)  (0) 2019.01.21
    Stack과 Queue  (0) 2019.01.16
    Array와 Dynamic Array, Linked List  (0) 2019.01.11
    ADT(Abstract Data Type)  (0) 2019.01.09

    스택'자료구조'하면 대부분이 떠올리는 선형 자료구조이자 ADT이다. 둘 다 값을 삽입하는 push와 값을 제거하며 반환하는 pop 명령을 가지며(다르게 부르기도 함), 자료구조가 비어있는지를 확인하는 empty, 원소가 얼마나 들어 있는지를 확인하는 size 명령이 구현되기도 한다. 스택과 큐는 한가지 큰 차이점이 있는데, 스택은 LIFO(Last In, First Out), 큐는 FIFO(First In, First Out) 구조다. 얼핏 생각해 보면, 스택의 특징인 '가장 최근에 들어간 원소부터 꺼내진다'는 굉장히 쓸데없어 보이지만, 꽤 많은 곳에서 사용된다.

    • 함수의 call stack : 함수는 호출되었을 때 스택 프레임을 생성하여 call stack에 push되고, 함수가 종료되면 call stack에서 pop된다. 따라서, main -> a -> b -> c 순서로 함수를 호출하면, 순서대로 스택 프레임이 push되고, 가장 최근에 쌓인 스택 프레임부터 pop되어 c -> b -> a -> main 순서로 제어가 돌아온다.
    • 브라우저의 히스토리 : 브라우저는 사용자가 방문한 페이지들을 차례로 스택에 push한다. '뒤로가기' 버튼을 누르면, 히스토리에서 주소를 pop하여 보여준다.

    큐는 백엔드를 경험해본 사람이라면 익숙하겠지만, 작업 큐에서 자주 쓰인다. 그 외에도 정말 많은 곳에서 쓰인다. 예를 들자면, 운영체제의 스케줄링 알고리즘 중 FCFS(First-Come, First-Served) 스케줄링도 큐(정확히는 큐의 메커니즘인 FIFO)가 쓰인다.

    설명만 무작정 하면 재미 없으니, Go로 스택과 큐를 구현하는 것으로 글을 마무리한다. Slice를 단지 써먹은 데에 지나지 않으므로, 진지하게 구현하려면 메모리를 직접 할당하는 방식(C의 mallic, realloc 등..)을 사용하자.

    '컴퓨터 기초 > 자료구조' 카테고리의 다른 글

    Tree의 표현(representation)  (0) 2019.01.21
    Tree  (0) 2019.01.18
    Array와 Dynamic Array, Linked List  (0) 2019.01.11
    ADT(Abstract Data Type)  (0) 2019.01.09
    [자료구조] AVL Tree  (0) 2018.09.09

    이번엔 API 설계 원칙과 직렬화 포맷을 결정한다. 대부분의 경우 이 두가지는 '당연히 REST랑 JSON 아님?' 하며 관례적으로 결정하고 넘어가곤 하지만, 빼먹지 말고 이것도 의사결정 과정을 끼워 두자. 사실 이 앞에 프로토콜을 결정하는 챕터를 넣어두려 했는데, 거기까진 너무 TMI인 것 같아서 나중으로 미뤘다. 프로토콜은 일단 현재로선 일반적으로 사용되는 HTTP/1.1을 사용하는 것으로 하자. 물론 추후에 HTTP/2에 대한 이야기도 할 예정이다.

    API 설계 원칙의 도입 이유

    우리는 결론적으로 웹 어플리케이션 서버를 개발하고 운영하는 것이 목적이므로, '이렇게 HTTP 요청을 보내면, 이렇게 응답해준다'라는 스펙을 기능에 따라 설계해야 한다. API 설계 원칙은 웹 서버의 API 스펙을 어떤 규칙에 따라 정의할 것인지를 나타낸다.

    • 'GET /post는 게시글 목록을 불러오고, GET /post/{id}는 특정 게시글의 내용을 불러온다'같은 설계는 다 아키텍처 기반으로 결정하는 것이 좋다. 아키텍처가 없다고 API 디자인을 못하는 것은 아니지만, 의사결정의 기반이 있는 것이 좋기 때문이다. 나는 옛날에 이런 걸 몰랐어서 모든 요청을 싹 다 POST /로 받고, 101, 102와 같은 동작 code를 가지고 로직을 분기시켰다. '미친놈인가'하는 소리 안 들으려면 아키텍처 베이스가 있어야 한다.
    • '잘 디자인된' API는 불필요한 커뮤니케이션 비용을 줄인다.

    직렬화 포맷의 도입 이유

    직렬화 포맷에 대해 예를 들면, '게시글'을 나타내는 '제목'과 '내용' 데이터를 표현하기 위해 아래와 같은 방법들을 사용할 수 있다.

    데이터를 어떤 방식으로 표현할 지를 결정해 두어야, 클라이언트와 서버 간의 데이터 교환에서 혼란을 줄일 수 있다.

    의사결정 - API 설계 원칙 결정

    배경과 요구사항

    • 클라이언트 사이드에게 익숙한 아키텍처거나, 러닝커브가 감당 가능한 수준이어야 한다.
    • 개발 일정에 딜레이를 일으킬 여지가 있으면 안된다.

    선택지

    • HTTP API
    • REST API
    • GraphQL

    의사결정

    HTTP API를 선택하겠다. 그 이유는,

    • REST API가 명시하는 모든 원칙을 만족하는 API를 작성하는 것은 쉽지 않다. 결국은 '느슨한 REST' 느낌의 HTTP API가 되기 마련이다. 따라서 괜히 RESTful API 이러면서 깝치다가 정의구현 당하는 수가 있다. 제약조건을 따르던지 다른 단어를 쓰도록 하자. REST의 self-descriptiveHATEOAS 원칙은 만족하기 정말 어렵다.
    • REST는 HATEOAS(hypermedia as the engine of application state)라는 원칙을 지켜야 하는데, 이는 '어플리케이션의 상태가 Hyperlink를 이용해 전이되어야 한다'라는 의미다. 우리의 API는 미디어 타입이 JSON(JSON 형식의 데이터를 위주로 주고받는 형태)일텐데, HATEOAS를 지키기 어렵다.
    • API가 꼭 REST API여야 할 필요가 없다.
    • GraphQL은 사례가 너무 적다. 사용자 인증 처리 로직을 생각하는 것부터 고민이 많아진다.
    • GraphQL은 클라이언트 사이드에서 러닝커브가 생길 수 있다.
    • HTTP API가 이야기하는 아키텍처로 충분할 것이라고 판단했다.

    본인이 REST라는 걸 잘못 알고 있었나 싶다면, 그런 REST API로 괜찮은가라는 슬라이드 자료를 읽어보자.

    준비

    MDN의 HTTP 개요 문서, 이고잉님의 HTTP 강의 영상을 한번쯤 보면 좋을 것 같다. 'HTTP를 하나도 모르겠다'라면, 진도를 더 나가기 어려울 수 있으니 개발 잘하는 친구한테 물어보거나, 구글링을 하거나 해서 조금이라도 배경을 쌓고 넘어가자.

    의사결정 - 직렬화 포맷

    예를 들어, Kotlin로 이루어진 안드로이드 어플리케이션이 HashMap 객체를 문자열과 같이 특별한 형태로 가공해서 보내면, Node.js로 구성된 WAS가 이를 JavaScript 고유의 Object 타입으로 해석해 사용할 수 있어야 한다. 이를 위해 표준화된 직렬화 포맷이 여럿 존재하며, 그들 중 어떤 포맷을 사용할 것인지를 결정하도록 하자.

    배경과 요구사항

    • 이것 또한 클라이언트 사이드에게 익숙한 아키텍처거나, 러닝커브가 감당 가능한 수준이어야 한다.
    • key-value 매핑array와 같은 요소의 나열 표현에 문제가 없어야 한다.
    • 충분한 수준의 데이터 타입을 커버할 수 있어야 한다. - 정수, 실수, 문자열, boolean 등

    선택지

    의사결정

    JSON을 사용하자. 그 이유는,

    • 동일한 데이터를 표현하더라도, JSON이 비교적 더 잘 경량화되어 있으며 가독성도 좋다.
    • XML의 tree 구조는 자원을 표현하는 데에 그리 효과적인 포맷은 아닌 것 같다고 판단했다.(Array의 표현이 어려움) 옛날엔 많이 쓰였다고 하는데, 주관적인 의견을 덧대자면 진짜 이거 왜 쓰는지 모르겠다. 아직도 공공 API의 일부가 XML을 쓰는 게 정말 놀랍다.
    • YAML관례 상 직렬화 포맷으로 잘 사용하지 않는다. 역직렬화 속도도 느리고, YAML을 썼을 때 생기는 메리트가 딱히 없다.
    • Protobuf구글에서 개발한 data exchange format이다. 직렬화/역직렬화 속도가 빨라 성능 상의 이점이 있고, .proto 파일을 정의하는 것만으로 validation rule들을 정리하고, 비교적 적은 노력으로 API 문서화에도 응용할 수 있으며, 클라이언트 단은 proto 컴파일을 통해 이들에 대응되는 클래스(DTO)들을 자동으로 정의할 수도 있어서 시도해볼 가치가 충분하다. 그러나 조직 내에 protobuf를 사용했을 때 문제가 없을지에 대한 신뢰 수준이 낮고, 우리의 서비스 규모가 크지 않으므로 protobuf에 대한 확신이 생기고 난 후에 바꾸더라도 스트레스가 크지 않을 것이라는 생각이다. 당장은 다들 익숙해 하는 JSON을 쓰고, 나중에 바꿔 보자.
    • JSONJavaScript Object Notation의 약자다. JavaScript나 그 형제들(TypeScript 등)로 로직 처리를 하게 될 프론트엔드에게 JSON만큼 편한 구조가 없으며, 모바일 앱과 웹을 포함해 대부분의 프론트엔드 엔지니어들은 이미 JSON에 익숙해져 있다.

    준비

    Stackoverflow의 'What is JSON and why would I use it?이라는 질문과, json.org의 JSON 개요 문서를 한 번 읽어 보자. 이번 챕터에서는 의사결정에 관례의 비중이 컸다. 기술 선택에 너무 의외성이 크면 조직 전체에 걸쳐서 기술부채가 생길 수 있어서, 관례를 감안할 수밖에 없었던 것 같다.

    지금까지

    • 버전 관리 시스템으로 Git을 사용하기 시작했다.
    • Git 웹호스팅 서비스로 GitHub를 사용하기 시작했다.
    • GitHub Issues와 Projects로 이슈 트래킹을 시작했다.
    • 개발 프로세스와 브랜칭 모델을 정립했다.
    • HTTP API 아키텍처 기반으로 API 스펙을 디자인하기로 했다.
    • JSON을 직렬화 포맷으로 결정했다.


    선형 자료구조인 배열(Array)연결 리스트(Linked List)는 데이터를 나열한다. 그러나 동일한 operation에 대해 내부적으로 수행되는 작업이 달라, 시간 복잡도 면에서 서로 반대의 장단점들이 존재한다.

    Array(배열)

    데이터를 논리적 순서에 따라 순차적으로 다룬다. 일반적으로 (메모리에서의)물리적 주소 또한 순차적이다. 또한 인덱스를 가지고 있어서, 무작위 접근(random access)이 매우 빠르다. 그러나 크기가 고정되어 있기 때문에 배열의 크기를 늘리는 연산이 필요한 경우가 있고, 데이터 삽입/삭제 시 그 다음 인덱스부터의 데이터를 모두 한 칸씩 뒤로 밀거나 앞으로 당겨오는 연산을 해야 하기 때문에 데이터 삽입과 삭제 시 느린 속도를 보인다.

    Dynamic Array(동적 배열)

    동적 배열은 크기가 고정되지 않은 배열을 의미하며, 일반 배열의 '크기가 고정되어 있다'는 문제를 제거한다. C++ STL의 vector, Python의 list 등이 동적 배열의 구현체다. 배열의 번외품처럼 보이지만, 꽤 많은 곳에서 유용하게 사용된다.

    Linked List

    데이터를 주소의 참조에 따라 순차적으로 다룬다. 배열에서 요소 하나가 값 + 인덱스로 이루어져 있다면, Linked List에서 요소 하나는 값 + next의 주소, 또는 값 + prev의 주소 + next의 주소로 이루어진다. 전자를 단순 연결리스트, 후자를 이중, 또는 양방향(doubly) 연결리스트라고 부른다. Linked List는 배열과 다르게 무작위 접근이 불가능하기 때문에, 특정 노드에 대한 임의 접근은 링크를 계속해서 따라가야 가능하기 때문에 배열보다 느리고, 반대로 데이터를 삽입/삭제할 때에는 앞/뒤 노드의 주소만 끼워넣을 노드의 주소로 바꿔주면 되기 때문에 배열보다 빠르다.

    '컴퓨터 기초 > 자료구조' 카테고리의 다른 글

    Tree의 표현(representation)  (0) 2019.01.21
    Tree  (0) 2019.01.18
    Stack과 Queue  (0) 2019.01.16
    ADT(Abstract Data Type)  (0) 2019.01.09
    [자료구조] AVL Tree  (0) 2018.09.09

    집합, 리스트, 스택, 큐, 트리처럼 자료구조 과목에서 흔히 볼 수 있는 것들을 추상적 자료형(Abstract Data Type)이라고 부른다. 이들은 공통점이 한가지 있는데, 자료 자체의 형태와 그 자료에 관계된 연산들이 수학적으로만 정의되어 있다는 것이다.

    • Stack은 삽입한 순서대로 쌓이는 값들의 모임이고, 스택의 제일 위에 값을 삽입하는 push와 제일 위의 값을 하나 빼서 반환하는 pop 연산이 있다. 스택이 비었는지 알아보는 empty 연산, 스택에 쌓인 값이 몇 개인지 알아보는 size 연산이 정의될 수 있다.

    형태와 연산이 수학적으로만 정의되어 있다. 구현의 세부사항은 전혀 정의되지 않았다. 따라서 스택이 내부적으로 array로 구현되는지, linked list로 구현되는지, size 연산을 수행할 때 원소의 개수를 일일히 세는지, 아니면 개수를 따로 저장해 두는지와 같은 세부 사항들은 다뤄지지 않는다. 따라서, ADT는 구현 부분을 나타내지 않고 순수한 기능이 무엇인지 나열한 것이다. Java에서 추상 메소드의 집합인 interface와 비슷한 맥락이다.

    '컴퓨터 기초 > 자료구조' 카테고리의 다른 글

    Tree의 표현(representation)  (0) 2019.01.21
    Tree  (0) 2019.01.18
    Stack과 Queue  (0) 2019.01.16
    Array와 Dynamic Array, Linked List  (0) 2019.01.11
    [자료구조] AVL Tree  (0) 2018.09.09

    이번엔 백엔드 포지션에서 어떤 방식으로 개발을 진행할지를 결정하자. 개발 프로세스 정립에 대해 두 가지의 의사결정을 진행할 것이다.

    도입 이유

    개발 프로세스는 어떻게 이슈를 관리하고, 어떤 방식으로 작업을 진행하고, 완료된 작업은 어떤 과정을 거쳐서 실제 제품에 반영시킬지와 같은 것들을 규칙화시킨 것이다. 정립이 필요한 이유는 다음과 같다.

    • 프로젝트 관리자 입장에서 각 개발자에게 할당된 작업들이 어떤 상태인지(준비 중/진행 중/완료)를 쉽게 알 수 있다.
    • 이슈를 실제로 처리하는 입장에서, 개발 프로세스에 따라 작업을 진행하면 어느 브랜치에서 어떤 이름으로 브랜치를 생성할지/어느 브랜치로 pull request를 올리며 리뷰를 받아야 할지/master merge 후 어떤 후속 작업을 진행해야 하는지와 같은 고민을 줄일 수 있다. 실제로 작성하는 코드에만 집중하는 환경을 만들 수 있다.
    • 잘 정립된 개발 프로세스는 생산성을 높이는 것에 기여하며, 작업의 진행을 매끄럽게 만들고, 결과물의 퀄리티를 높인다.

    이 의사결정에는 별도의 선택지가 없다. 스크럼이고 칸반이고 하는 정해진 프로세스가 많긴 하지만, 조직의 상황에 딱 맞는 개발 프로세스를 찾기는 어렵기 때문에, 기존의 개발 프로세스를 기반으로 하나씩 직접 정립해 보자.

    의사결정 - 이슈 관리

    이슈 관리 도구를 결정하자. 이슈는 작업이라고 생각하면 된다. 작업을 리스팅하고, 작업을 처리할 작업자를 할당하는 등의 일을 위해 사용한다. 대부분의 조직은 이슈를 기반으로 움직이고, 나도 이런 흐름이 좋다고 생각한다. 이슈 관리 도구는 이슈 트래커라고도 부른다. JIRA를 쓰기도 하고, Trello를 쓰기도 하고, Google 스프레드시트를 쓰는 조직도 봤던 기억이 있다. 나는 과거에 Asana를 많이 썼었다.

    배경과 요구사항

    • 이슈 각각에 대해 작업자를 assign할 수 있다.
    • 이슈에 대해 커뮤니케이션할 수 있어야 한다.
    • 태그/라벨 등으로 이슈의 종류를 구분할 수 있어야 한다.(feature, hotfix, enhancement 등)
    • 이슈의 상태(To do, In progress, Review requested 등)를 구분해서 시각화할 수 있어야 한다.
    • 되도록 GitHub 내에서 해결할 수 있으면 더 좋다.

    선택지

    의사결정

    GitHub Issues & Projects를 선택하겠다. 그 이유는,

    • Jira는 무조건 돈이 들어간다. 그렇다고 큰 메리트가 따로 있다고 생각이 들진 않는다. 큰 조직에서 매니저 급이 태스크를 관리할 때나 잘 쓰일 것 같은 트래커라고 생각한다.
    • Trello는 모던한 형태의 태스크 관리 보드인데, 필요한 기능들이 잘 들어가 있고 괜찮은 툴이지만 지금의 요구사항으론 GitHub 외부의 트래커를 따로 쓸만한 이유가 없다.
    • 위의 요구 사항을 모두 만족한다 - assign, issue conversation 기능이 있고, customized labelfilter by label 기능을 통해 이슈 종류 구분이 원활하고, projectsissues 기능을 연동해 이슈를 시각화 가능하며, projects의 automation 기능을 통해 이슈 구분이슈 상태 자동 갱신도 가능하다.(이슈가 close되면, projects에서 'Done' 컬럼으로 자동 이동시키는 등)
    • GitHub Issues에서 이슈를 등록하면 해당 이슈에 대한 번호가 매겨지는데, 커밋 메시지에 #123과 같이 그 번호를 명시해 두면 자동으로 해당 이슈가 링크되며 conversation할 때도 사용할 수 있다.(참고로 이건 Jira와 BitBucket을 연동하는 경우에도 가능하다.)
    • 이슈 트래커를 위해 따로 돈을 내지 않아도 된다. private repository를 생성 가능하게 만들기 위해 Organization 유료 플랜을 결제하는 경우는 있을 수 있지만, GitHub issues와 projects는 항상 무료다. 아래의 캡처들은 순서대로 각각 ProjectsIssues 기능이다.

    준비

    1. 저번에 만들어뒀던 레포의 Projects 탭에 들어가 새로운 프로젝트를 하나 만든다. 프로젝트 템플릿은 아무거나 고르자. 나는 None으로 설정하고 직접 구성하려고 한다.
    2. Projects에서 Column을 원하는 대로 만들고 automation을 설정하자. 컬럼을 만들 때 보이는 'Automation'이나, 만들어진 컬럼의 우측 상단에 있는 ... 버튼으로 드롭다운을 열고 'Manage automation' 버튼을 누르면 보여지는 화면에서 Automation preset과 그 하위 카테고리를 선택하여 자동화를 설정할 수 있다. 나는 새롭게 생성된 이슈To Do, 리뷰가 요청되어 approve를 기다리고 있는 pull requestReview Requested, approve된 pull requestReviewed, close된 이슈Done이라는 이름의 컬럼에 자동화할 것이다. 위의 자동화들은 모두 automation preset에서 제공하는 기능이니 잘 찾아서 추가하기 바란다. 진행 중인 작업을 분류하기 위해 In Progress나, 나중에 조직이 커지며 품질 관리 과정이 생긴다면 Pending QA같은 컬럼이 추가될 수도 있을 것이다. 필자와 동일하게 해둘 필요 없으니, 입맛대로 만들기 바란다.
    3. 이슈에서 사용할 custom label을 설정한다. Issues 탭에서 Labels 화면에 들어가면 된다. 사람마다 다른데, 나는 우선순위 관점에서 minor/major, 종류 관점에서 feature/bug/enhancement 정도로 나눠둘 것이다. 참고로 필자가 평소에 프로젝트를 할 때는 major/minor/urgent, Non-code work/On-code work/Discussion 정도로 라벨을 만들어 둔다. 이건 나중에 필요할 때 추가하면 되니 대충 하고 넘어가자.
    4. 위에서 만든 프로젝트를 지정한 채 이슈를 생성하고 close했을 때 이게 Projects에 잘 동기화되는지 확인한다. 아래는 각각 이슈 라벨 설정, 이슈 아이템, automation이 설정된 Projects 화면이다.

    의사결정 - 개발 프로세스 정립

    이슈의 생성부터 작업이 제품에 반영되기까지의 사이클을 정리하자. Git에서 사용할 브랜칭 모델도 함께 정리될 것이다. 우리가 위에서 했던 걸로 따지면, 이슈는 최초에 'To Do'에 입력되고, 작업의 진척에 따라 오른쪽 컬럼으로 이동하는 식이기 때문에 프로세스는 칸반 기반이라고 생각하면 될 것 같다. 칸반에 대해서는 잘 가요 스크럼, 반가워요 칸반이라는 글을 보면 좋을 것 같다.

    배경과 요구사항

    • 너무 복잡할 필요가 없다.
    • 개발 프로세스를 브랜치 lifecycle로 대응(ex. 작업 시작은 브랜치 생성, 작업 완료 후 브랜치 제거 등)시킬 수 있어야 한다.

    의사결정

    개발은 아래처럼 진행해보려 한다.

    1. 개발자의 공수가 필요한 작업이 생기면, 알맞는 라벨과 함께 이슈를 생성하고, 프로젝트 보드와 동기화시키기 위해 Projects를 선택해 둔다. 이슈가 'To Do'에 위치할 것이다.
    2. 해당 작업을 진행할 개발자가 정해지면, assign해 둔다.
    3. 개발자는 issue/<이슈 번호> 네이밍을 가진 브랜치를 master에서 체크아웃한다. 이슈 번호가 9번이라면, issue/9같은 식이다.
    4. 작업이 완료되면, master 브랜치로 pull request를 올린다. 이후 reviewer 기능을 통해 동료 개발자에게 리뷰를 요청하고, Projects를 선택해 둔다. pull request가 'Review Requested'에 위치할 것이다.
    5. 리뷰가 approve되면 merge하고, 문제 없이 작업이 완료되었다면 issue를 close하고 작업 브랜치를 제거한다. pull request는 'Reviewed'로, 이슈는 'Done'으로 이동할 것이다.

    pull request를 올리지 않고 그냥 git merge 커맨드로 병합한 후 push하는 방법도 있지만, 리뷰 과정을 끼워두는 것이 더 안정적이다. 리뷰가 없다면 변경을 작업자만 알게 될 것이기 때문이다. 또한 작업용 브랜치를 체크아웃하는 이유는 master 브랜치의 코드는 항상 '현재 서비스에 적용되어 있는 상태'로 두려고 하기 때문이다. master의 코드는 항상 안정적인 상태로 둔다는 것이다. 나도 옛날엔 그냥 master에서 무작정 다 작업하고 그랬었는데, 대부분의 경우 이런 버릇은 좋지 않다.

    위처럼 master에서 이슈 브랜치를 따는 것과 같은 룰을 브랜칭 모델이라고 부른다. git-flow, github-flow 이런 룰들이 많은데, 일단 우린 간단하게 master와 이슈 브랜치만 다뤄보는 걸로 시작하자. 브랜칭 모델에 대해 더 알아보고 싶다면 A Successful Git Branching Model (번역) 글을 읽어보자.

    지금까지

    • 버전 관리 시스템으로 Git을 사용하기 시작했다.
    • Git 웹호스팅 서비스로 GitHub를 사용하기 시작했다.
    • GitHub Issues와 Projects로 이슈 트래킹을 시작했다.
    • 개발 프로세스와 브랜칭 모델을 정립했다.


    주석(comment)은 설명문이다. 문학 책에 실려 있는 소설에서, 어려운 단어가 나오면 페이지의 맨 밑에서 뜻을 풀어 알려주는 것처럼 말이다. 남이 짠 코드를 본 적이 있거나, 내 코드를 남이 본 경험이 있다면 주석이 왜 중요한지 알 것이다. 코드의 이해를 돕기 때문이다. 주석으로 나쁜 코드를 회피하지 말라고 하지만, 아무리 가독성에 신경쓴다고 하더라도 코드의 복잡도가 늘어나면 주석 없이는 의도를 파악하기 힘들기 마련이다.

    대부분의 프로그래밍 언어들은 일반적으로 한 줄짜리 주석에 slash(/)를, 여러 줄짜리 주석에 slash와 asterisk(*)를 섞어 사용한다. Java의 예를 보자.

    그리고 아래는 Python의 예다.

    한 줄짜리 주석에는 내용의 맨 앞에 sharp(#) 기호를 사용하고, 여러 줄짜리 주석에는 내용에 세 개의 double quote(""")를 감싸 표현한다. 대부분의 경우 #을 사용하는 전자의 표현식을 사용한다. 참고로 쉘 스크립트Ruby에서 주석을 #으로 표현한다.

    후자의 주석 표현에서 사용하는 세 개의 double quote로 감싸진 문자열은, 사실 파이썬에서 문자열 리터럴 표현식이기도 하다.(파이썬에는 4가지의 문자열 리터럴 표현식이 있다 : ', ", ''', """) 따라서 값으로써 관리하기에 더 유연하기 때문에, 파이썬에서는 이를 함수나 클래스 정의의 맨 위에 사용하여 docstring(문서화 문자열)이라는 이름의 주석으로 사용하도록 권고한다. 실제로 #을 사용한 주석은 런타임으로 넘어가며 무시되지만, """를 사용한 주석은 런타임에서도 그대로 유지된다. 결론은, 여러 줄에 걸쳐 주석 처리를 하는 것을 포함한 일반적인 경우에는 #을 사용하는 것이 좋다는 것이다.

    '프로그래밍 > Python' 카테고리의 다른 글

    산술 연산자  (0) 2019.01.28
    숫자 자료형과 리터럴  (0) 2019.01.25
    파이썬의 타입 시스템과 빌트인 타입, 변수 선언  (0) 2019.01.23
    Hello World, 세미콜론은 optional하다?  (0) 2019.01.03
    개요와 설치  (0) 2019.01.01

    이번엔 버전 관리 시스템과, 이러한 버전 관리 시스템을 사용하는 프로젝트를 지원하는 웹호스팅 서비스를 결정해보도록 하자. 각각 Git, SVN / GitHub, BitBucket같은 것을 예로 들 수 있다. 소스코드 형상관리 시스템이나 소스코드 관리 시스템 같은 명칭으로도 종종 불리는데, 나는 그냥 버전 관리 시스템이라고 부르는 게 더 편한 것 같다.

    버전 관리 시스템의 도입 이유

    아무튼 이러한 버전 관리 시스템을 도입하려는 이유는 다음과 같다.

    • 백엔드 어플리케이션을 지금 당장은 혼자 개발하겠지만, 미래에 새로운 백엔드 엔지니어가 우리 팀에 들어와서 협업하게 될 수도 있다. 시스템의 도움을 받아 협업을 원활히 하는 것이 첫 번째 이유다. 소스 코드를 USB나 메일로 주고받는 건 엄청난 낭비니 말이다.
    • 변경을 쉽게 되돌릴 수 있다. 에디터 툴들이 일반적으로 지원하는 Ctrl+Z 커맨드와는 차원이 다른 수준의 기록 관리를 지원한다. 소스코드를 과거의 특정 시점으로 되돌리거나, 특정 시점의 변경 사항을 취소하거나, 두 버전의 소스 코드를 비교하는 등의 일이 가능하다.

    잘 모르겠다면 생활코딩 git 강의의 '버전관리란?' 챕터 영상를 살펴보자.

    버전 관리 웹호스팅 서비스의 도입 이유

    • 협업하고 있는 코드를 저장할 서버가 필요하기 때문이다. 이게 없으면 작업을 서로 공유하기 어려우며 사실 이 이유 하나만으로도 도입 동기가 충분하다. 코드를 USB나 메일로 주고받을 수도 있겠지만, 인생을 그렇게 낭비하지 말자. 버전 관리 서버는 직접 운영할 수도 있으나 에어비앤비, 슬랙, 페이팔같은 큰 조직도 GitHub을 쓰는 마당에 특별한 이유가 없다면 이미 잘 만들어져 있는 서비스를 쓰도록 하자.
    • 버전 관리 시스템을 지원하는 웹호스팅 서비스의 hook 기능을 통해, push나 이슈 추가, pull request같은 이벤트에 반응하여 자동으로 무언가(테스트 실행, 배포 등)가 실행되게 만들 수 있다. 예를 들면, master 브랜치로 push가 이루어졌을 때 자동으로 테스트를 돌리고, 문제가 없다면 배포하는 식이다.
    • 이러한 서비스들은 'pull request'라는 기능을 지원한다. 작업물의 병합을 요청하는 것이다. 여기서 리뷰를 진행하거나, 커뮤니케이션할 수 있다. 개발 프로세스 정립의 자유도가 높아진다.

    의사결정 - 버전 관리 시스템

    사실 요즘은 게임 업계가 아닌 이상 무조건 git을 쓰고있는 것 같지만, 이것도 의사결정에 포함시켜 보자.

    배경과 요구사항

    • 대부분의 개발자가 이미 익숙해져 있는 도구일 수록 좋다.
    • 브랜치 개념이 있어야 한다. 배포용 코드/작업용 코드 등으로 나누어 프로젝트를 원활히 진행하기 위해 꼭 필요하다.
    • 분산형 모델을 사용하는 것을 우선적으로 선택하겠다. 중앙집중식/분산형 버전 관리 시스템의 차이를 모르겠다면 Git SCM의 문서 버전 관리란?을 읽어보자.

    선택지

    • git
    • svn
    • mercurial

    의사결정

    나라면 git을 선택할 것 같다. 그 이유는,

    • SVN클라이언트-서버 모델을 사용한다. 이 때문에 저장소의 사본을 로컬에서 관리하는 git에 비해 매우 느리며(변경 로그 하나 보는 것도 인터넷을 경유해야 하므로) commit이 원격에 즉시 반영되기 때문에 부담이 크다. 게다가 SVN은 변화를 저장하는 방식으로 버전(변경 이력)을 관리하는데, 이것도 git과 비교했을 때 속도 문제를 일으킨다. git은 버전을 스냅샷으로 관리하기 때문이다.
    • 게다가 SVN에는 시스템적으로 브랜치 개념이 없어서, 작업마다 폴더를 만드는 식으로 우회하곤 한다. 스냅샷 기능이 없기 때문.
    • MercurialGit분산 버전 관리 시스템이라는 점에서 꽤 유사하지만, 대부분의 개발자는 Git에 더 익숙하다. 우리 조직에 누군가가 들어왔을 때 불필요한 러닝커브가 생기지 않았으면 하는 바람이고, Git과 비교했을 때 Mercurial이 큰 메리트가 있는 것도 아니라는 판단이다.

    준비

    Git은 MacOS를 비롯한 대부분의 리눅스 환경에서 기본으로 제공한다. Windows를 사용하는 경우에만 Git for Windows를 설치하면 된다.

    의사결정 - Git 웹호스팅 서비스

    Git이 참조할 원격 저장소를 관리해줄 장소가 필요하다.

    배경과 요구사항

    • 이것도 대부분의 개발자가 이미 익숙해져 있는 도구일 수록 좋다.
    • private 저장소를 무료로 만들 수 있게 해주는 서비스를 우선적으로 검토할 것이다. 나중에 조직이 커지면 돈을 쓰는 게 맞겠지만, 지금은 과금에 대한 부담이 적을수록 좋을 것이니 말이다.
    • 자체적으로 가지고 있는 이슈 트래커(작업 관리 도구)가 있으면 좋다.

    선택지

    의사결정

    GitHub를 선택하겠다. 그 이유는,

    • BitBucket서버가 느리다. 이게 딱히 문제가 안 될 줄 알았는데 생각보다 굉장히 답답하며 자체적으로 제공하는 기능도 GitHub만큼 풍부하지 않다.
    • GitLab은 써본 사람이 얼마 없다. 이것도 딱히 문제가 안 될 줄 알았는데 예전에 GitLab 한 번 썼다가 어떤 기능이 어디에 있는 줄 몰라서 답답해 죽는줄 알았다. Mercurial을 쓰지 않았던 이유와 동일하게, 큰 메리트가 없으면서 굳이 익숙하지 않은 걸 썼다가 괜히 생산성만 조지는 수가 있다.
    • GitHub에 내장되어 있는 이슈 트래커(GitHub Issues)Projects 기능이나 ZenHub 플러그인과 함께 쓰면 생각보다 쓸만 하다. 조직이 웬만큼 커지기 전에는 이슈 트래커로 따로 돈 들어갈 일이 없다. 이슈 각각에 due를 설정하는 게 없는 것 빼고 꽤 만족스럽다.
    • GitHub은 free로 쓰던 돈을 내고 쓰던 거의 모든 면에 있어서 가장 풍부한 서비스라고 생각한다. 별의 별 기능이 다 있는데 그게 다 꼭 한 번씩은 필요한 기능들이었으며 기능 자체도 매우 잘 만들어져 있었다. 특정 브랜치를 향한 pull request에 대해 n개 이상의 리뷰를 받아야 merge할 수 있게 만드는 것이나, Organization에서 팀을 만들어 사용자를 포함시키고, 팀 단위로 저장소 각각에 read/write/admin 권한을 부여하고, 서브 팀을 만들어 더 세부적인 위계를 나누는 등, 팀 관리 면에서도 매우 뛰어났던 것 같다.
    • 서비스 개발을 진행하는, 특히 백엔드 저장소를 public으로 두면 다른 의미로 많은 관심을 끌 수 있으므로 private로 두는 것이 좋다. 이 때문에 한가지 걸렸던 점은, GitHub에서 free 계정은 visibility를 public으로만 설정 가능하다는 것이었다. GitLab과 BitBucket이 무료 계정에게도 private 저장소를 제공하는 것과 대비된다. 이를 위해선 개인이 한 달에 7달러씩 지불하는 developer plan에 가입하거나, 팀 단위로 organization을 만들어서 유료 플랜을 구입해야만 했는데, 최근에 GitHub가 무료 플랜에게도 private 저장소를 만들 수 있도록 과금 정책을 변경하며 이러한 고민이 당장은 사라졌다. 물론 나중에 조직이 커져서 organization 단위로 프로젝트를 진행하게 되면 돈을 써야겠지만, 이건 다른 서비스들에게도 똑같이 적용되는 이야기다. 따라서 돈 때문에 GitHub를 포기할 이유가 없다. 필자는 독자 여러분의 코드 참조를 위해 public으로 두겠다.

    준비

    GitHub에 계정이 없다면 만들고, 원격 저장소 하나를 만들어 두자. 원래는 팀 단위로 organization을 만들어서 저장소를 만드는 것이 일반적이지만, 같이 프로젝트를 하는 친구들은 사실 가상인물이므로 개인 계정에서 진행하자. GitHub Help의 Create a repo를 참고하면 될 것 같다. 위에서도 말했듯 필자는 독자 여러분의 코드 참조를 위해 저장소를 public으로 만들텐데, 실제로 프로젝트를 하는 중이라면 private로 만드는 것이 맞다.

    의사결정 - Git GUI

    참고로 이건 조직 내가 아니라 개인적으로 결정해야 할 일이다. CLI를 쓰겠다면 존중하겠지만 나는 그 많은 git 명령어를 자유자재로 외울 자신이 없기에 GUI를 사용하겠다. git 명령어가 익숙치 않은 독자가 git을 조금 더 쉽게 시작할 수 있게 하기 위한 목적도 있다.

    배경과 요구사항

    • GUI 단에서 Git이 가지고 있는 대부분의 기능을 모두 사용할 수 있어야 한다.
    • UX가 후지지 않아야 한다.
    • GUI 툴 쓰다가 답답해서 콘솔 여는 경우가 없어야 한다.

    선택지

    의사결정

    조직 단위의 결정이 아니기에 매우 개인적인 관점에서 GitKraken를 선택하겠다. 그 이유는,

    • GitHub Desktop은 가볍고 깔끔하지만 Git의 모든 기능을 지원하지 않는다. 예전에 신규 기능을 개발하다가 버그 핫픽스 작업이 갑작스레 생겨서 git stash 명령을 찾아보려 했는데, GitHub Desktop은 이걸 지원하지 않아서 CLI의 도움을 받았던 적이 있다.
    • 일할 땐 웬만하면 SourceTree를 쓰고, 나름 만족스럽지만 이번에 GitKraken을 찾아보니 UI도 예쁘고 좋아 보였다. 맨날 쓰던 것만 쓰면 꼰대가 되지 않을까 싶어서 이번에 GitKraken을 새로 써보려고 한다. GUI 툴은 쉽게 바꿀 수 있으니 써 보다가 아니다 싶으면 SourceTree로 바꾸려는 생각이다.

    준비

    다운로드를 받아 설치해 두자. 그리고 처음 실행하면 'Sign in with GitHub' 버튼을 통해 본인의 GitHub 계정을 연동할 수 있다. 물론 내가 GitKraken을 쓰기로 결정했다고 해서 꼭 이걸 써야 되는 건 아니다. 말했듯이 조직 단위의 결정이 아니기 때문이다.

    그리고 Git GUI를 쓰던 bash를 쓰던 접근하기 편한 장소에 위에서 만들어 뒀던 레포를 clone해 두자. 나도 내 개인 계정에 Sampleapp-for-blog라는 이름의 저장소를 만들고, GitKraken을 통해 clone받았다.

    지금까지

    • 버전 관리 시스템으로 Git을 사용하기 시작했다.
    • Git 웹호스팅 서비스로 GitHub를 사용하기 시작했다.


    + Recent posts