두다지 서버 개발자 양성 프로그램 Week 8

8번 째 멘토링

네트워크의 Transport Layer와 Network Layer에 대한 공부를 마치고, 그에 대한 정리를 멘토링을 통해 하였다. 또한 지난 주에 과제로 제출되었던 UDP로 TCP 구현하기 과제의 일부를 해서 갔다. 네트워크를 공부하며 느끼는 점은 물리적 계층에 가까워지는 Layer일수록 그 깊이가 더 깊어 이해하기가 힘들다는 것이다. 아직 Link Layer는 추가적으로 학습하지 못했지만 지금까지 이해한 것보다 어려울 것 같다는 느낌을 강하게 받았다.. 작년부터 전산학을 공부함에 있어 네트워크가 계속 발목을 잡는 듯한 느낌을 받는다. 앞으로 서버 개발자를 목표하기 위해서는, 멘토링 이후에도 네트워크는 추가적으로 공부를 더 해서 제대로 이해하고 넘어가야 할 것이다.

학습한 것에 대한 리뷰

  • UDP는 socket을 통해 1:1 연결을 하기 때문에 header에 2가지 정보를 포함해야 함
    • destination IP, destination Port number
  • TCP는 welcome socket 하에 1:n의 연결을 하기 때문에 header에 4가지 정보를 포함해야 함
    • destination IP, destination Port number, source IP, source Port number
  • Network Layer에서 IP protocol은 비신뢰적 전송
  • 네트워크 지원 하의 혼잡 제어는 single bit 전송을 통해 이루어짐
  • 2-way handshaking의 경우, Client의 중복된 conn_req를 Server가 Client의 종료 이후 받게 되면, Server가 socket을 열게 되는 half open connection의 문제가 발생할 수 있음
  • c언어 헤더 include 순서
    • 내 헤더를 가장 마지막에 작성해줌
  • debugging을 위해 logger 사용하자
  • 항상 unittest 하기
    • 내 프로그램이 잘 작동한다는 것은 unittest로 검증한 후에 시나리오 대로 동작시켜보아야 함
  • C에서 fread는 읽어온 것만큼만 전달하도록 readcount 변수를 추가적으로 사용해주어야 함

Computer Network

  • Transport Layer는 process 간 연결을 도와주는 계층
  • Application 단에서 Transport 계층에게 명령하여 상대 host에게 데이터를 전송
  • 왜 3-way handshake / flow control / congestion control을 하게 될까?
    • 서로 다른 계층에서의 성능 보장을 신뢰하지 못하기 때문
    • 네트워크 통신에서의 변수가 통제된 상황에서는 무조건 성능의 보장 가능
      • 그러나, 현실 네트워크는 그렇지 못하기 때문에 통제 되지 않은 상황을 고려한 프로토콜이 필요한 것
  • 전송 과정에서 거치는 hop의 성능을 알지 못하는 윗단
    • Transport Layer에서는 그저 최선의 상황을 가정해서 전송하는 것
  • Congestion control에서는 doubling의 threshold를 어떻게 잡을 까에 대한 문제 있을 수 있음
    • 결국, threshold를 잘 모르기 때문에 doubling을 구현하는 것
    • 일종의 땜빵코드(best effort..)
    • 절반으로 줄이는 TCP Tahoe도 같은 개념
    • Packet loss는 일종의 congestion 발생했다는 signal 처럼 이용되는 것
  • 네트워크 내의 여러 상황 때문에 General Purpose 상황에서 네트워크 이용률을 50%까지 올리는 것도 매우 어려운 일
  • 2-way handshake에는 다양한 문제가 있음
    • half open connection / doubly connected...
    • Server가 연결에 대해 보낸 ACK와 ACK 이후 즉시 보낸 data의 순서가 보장되지 않음
    • 따라서 서로가 정상적으로 연결(혹은 연결 해제)되었다는 것을 인지하고 communicate하는 것이 좋음!
      • cf. close는 4-way handshake
      • close 상황이 완전히 이루어지기 전에 유실되었던 segment의 전송 등을 고려하여 연결의 종료가 이루어짐

다음 주 까지의 assignment

  • 네트워크 과제 마무리
    • Packet에 TCP header 부여
    • listen() for. 3-way handshake 구현
    • flow control 구현 (순서 보장)
    • Fake Router 구현
      • Get datas from Client & send datas to Servers


두다지 서버 개발자 양성 프로그램 Week 7

7번 째 멘토링

컴퓨터 네트워크의 단골 손님인 OSI 7계층에 대한 전반적 이해와 Application Layer를 학습한 후, 학습한 내용에 대하여 리뷰하는 시간을 가졌다. 멘토링 시간을 위해 컴퓨터 네트워크라는 과목을 다시 학습해보니 작년에 수업을 통해 배울 때 보다 양이 더 방대하게 느껴졌고, 세세한 이해를 필요로 하는 부분이 많아 한 계층만을 학습하는데에도 오랜 시간이 소모되었던 것 같다. 멘토님께서는 네트워크 전문가로의 길을 갈 것이 아니라면, 각 계층의 특징과 계층 간 이동의 flow를 이해하는 정도로 충분하다고 하셔서 심도 깊은 이해보다는 각 계층에 대한 특징에 초점을 맞추고 학습했던 것 같다. 그리고 멘토링 시간을 통해 멘토님께서 그림을 통해 네트워크의 전체적 전송 flow를 잘 설명해주셔서 이해에 더 도움이 되었다.

학습한 것에 대한 리뷰

  • 네트워크는 정보 전달에 가장 큰 목적이 있음
  • 실생활의 '등기우편'도 컴퓨터 네트워크와 같은 개념
    • 상대방이 잘 수령했는지 여부까지 알 수 있는 송신법
    • 분류 과정에서 각 branch는 자신이 관심 있는 부분만 본다!
  • 각 계층 역시 패킷의 내용 중에서 관심있어 하는 '특정' 부분의 정보만을 선택해서 보는 것
  • Socket은 일종의 Module이자 API
    • 프로세스 내에 존재하는 것이며, User가 Queue에 쌓인 데이터 상황을 보며 사용

Computer Network

  • CSMA/CD는 같은 LAN에 물리는 host간 경쟁적으로 LAN선 사용하기 위한 방식
  • 같은 LAN선에 물린 host들 간에는 서로 패킷이 공개되는 문제가 있을 수 있음
    • 네트워크 카드까지 해당 패킷 들어오나, 운영체제단에서 header(dest IP) 확인하고 Drop 혹은 Take
    • 커널 튜닝을 통해 같은 LAN선의 데이터 모두 Sniffing 가능할 수도 있음
  • 같은 LAN선에 물린 host들 간 데이터 전송은 LAN 하나에서 모두 처리되기 때문에 빠를 수 밖에 없음
    • 따라서 서버 간 데이터 전송이 잦은 host들은 같은 LAN선에 물리게 네트워크 설계
  • Switch는 대개 그저 자신에게 가장 가까운 Router에게 패킷 전송
    • Router가 RIP 등의 프로토콜 통해 Routing 수행
  • ISP 간 통합이 되지 않기 때문에 Routing의 효용이 떨어짐(ISP is for business)
  • 자기 자신에게 온 패킷을 받지 않는 경우도 있을 수 있음
    • 받고 싶을 경우, Listen을 수행해야 함
    • Port의 개념이 등장한 이유
      • 해당 Port number로 전달된 패킷만 허용하겠다는 의미
      • 항상 destination 주소와 Port number가 함께 움직임!
      • 기본적으로 HTTP의 경우 80번 Port를 default Port number로 사용
  • UDP의 특징: 아예 못 받거나, 보낸만큼 다 받거나
    • 패킷의 수신 순서를 전혀 알 수 없음
    • Streaming Service, Interactive game에서 캐릭터의 위치 정보 등에는 사용하기 좋음
      • 즉, 마지막 패킷 정보가 중요한 서비스에 적합
  • 그러나, 대부분의 경우가 그렇지 않음 -> 'TCP를 사용해야하는 Case들'
  • TCP는 받는 쪽에서 일정 Port number로 Listen을 하고 있음
    • 보내는 쪽은 TCP Connection 확인(3-Way handshake)
    • 연결되면 이후부터 패킷 전송
  • Connection은 양측에서 socket을 생성하는 상황
    • 각 Socket은 내 IP 정보, 상대 IP 정보를 담고 있음
    • Client는 Server로 부터 어떠한 Port number로 전송할 것이라는 정보를 받음
        1. C -> S : 나 TCP Connection 할래
        1. S -> C : 그래. 나는 너에게 정보를 보낼 일이 있으면, '9999' Port number로 정보를 전송할게
        1. C -> S : 알겠어. 지정해준 Port number도 확인했어
  • 그러나 TCP Connection 이후에도 Dead Connection의 문제가 있을 수 있음
    • heart beat packet을 보내는 이유
    • heart beat의 확인을 통해 연결이 still alive 상황인지 확인할 수 있음
  • 전송하고자 하는 패킷이 너무 크면 번호를 붙여 Packet을 나누어 전송
    • socket.write(2048)하면 실제로는 규약에 따라 일정 크기(ex. by 128 bytes)로 나누어 번호를 붙여 전송
    • socket.read(2048)하면 buffer에 채워진 정보를 읽어올 수 있음
      • socket.read(1024)하면 buffer의 앞 부분을 읽어오게 됨
      • socket.read(10000)하더라도 2048 bytes 만큼만 보여줌
  • 전송 과정에서 패킷 유실의 문제도 있을 수 있음
    • TCP의 flow control을 통해 순서가 보장된 buffer를 전송 받을 수 있게 됨
    • Go back N, Selective Repeat 등의 알고리즘을 통해 순서 회복 가능
  • 받는 쪽에서의 해석이 중요하기 때문에 인코딩, 디코딩 개념이 등장
    • 우리는 어떻게 보내겠다라는 약속을 해야 함
      1. Parser를 이용
      • 서로 보낼 정보가 무엇인지 아는 가정하에 특수문자 등을 이용하여 전송
      • ex) A, 90, 80, 70 / B, 90, 50, 60
      1. 각 정보를 전송 크기 만큼 채워서 전송
      • ex) A, 90, 80, 70
      1. Application header에 Packet key값과 길이 정보 전달
      • 특정 길이만큼은 해당 key값에 들어가야하는 정보라고 명시
  • JSON의 경우 줄바꿈을 기준으로 Parsing
    • JSON.parse()는 socket.readline()와 같은 개념
    • 성능이 뛰어난 방법은 아니지만 굉장히 편리하고 유연하기 때문에 널리 사용
  • HTTP도 status code, header code 그리고 body code를 줄바꿈을 기준으로 parsing

다음 주 까지의 assignment

  • UDP 소켓 프로그래밍으로 TCP 구현
    • UDP로 보내지만 TCP와 같이 동작하도록!
    • socket과 buffer 사용
    • FakeRouter도 하나 생성 -> 하나의 Process
      • 정해진 길이만큼 Router에 write
      • Router는 일정 확률(5-10%)로 Packet Drop
      • 이후 TroubleShooting(flow control, sequence..)
    • Magic Port Number(ex. 50000) 사용
  • Transport Layer, Network Layer 정리


두다지 서버 개발자 양성 프로그램 Week 6

6번 째 멘토링

운영체제 남은 후반부를 학습한 후, 학습한 내용에 대한 정리를 하였다. 또한 운영체제 후반부에서 가장 중요한 부분 중 하나인 Concurrency Control(병행 제어)의 경우, 단순 학습에서 그치는 것이 아니라 Spinlock, Semaphore, Producer & Consumer Problem을 직접 소스 코드를 통해 구현해보며 해당 챕터에 대한 이해를 더할 수 있었다. 이 중 Spinlock의 경우, 초반에 이해를 잘못하여 올바른 소스 코드를 작성하지 못하였지만 다른 두 문제의 경우 의도한 대로 잘 작성할 수 있었다. 멘토링 시간에는 정리한 내용에 대한 이야기와 작성한 소스코드에 대한 리뷰를 진행하였다.

학습한 것에 대한 리뷰

  • Block & Wake-up Semaphore는 특정 PID에 Signal을 보내는 방식으로 구현 가능
    • 즉, Wake-up에 대한 책임은 현재 Running 중인 프로세스에게 있음
    • 이 때의 Semaphore는 Lock보다는 Signal을 위한 용도에 가까움
  • Wake-up의 방법 2가지
    • Blocked 된 프로세스들 중 하나를 Wake-up
      • 어떤 프로세스를 깨울 것인가에 대한 문제 존재
    • Blocked 된 프로세스들 전체를 Wake-up
      • 하나의 프로세스만을 running으로 변경하고, 다른 프로세스들을 다시 Blocked 시켜야 하는 Overhead 존재
  • Producer & Consumer Problem에서 2개의 Semaphore를 사용(Event-driven)하면 필연적으로 Busy Waiting보다 느리게 됨
    • CPU를 다 사용하면서, Lock-Free Queue를 사용하면 성능 향상 가져올 수 있음 -> But, Rare case
  • Semaphore에서 자원을 가감해주는 연산(R, V) 역시 공유 자원을 건들이는 것이기 때문에, 연산 전/후에 짧은 Busy-waiting을 통해 Lock을 걸어주어야 함

Operation System

  • Peterson's Algorithm은 현대에 더 이상 사용되지 않음
    • 현재는 Test_and_set과 compare_and_swap의 atomic한 연산을 사용
      • Test_and_set을 while문으로 기다려주는 것이 가장 쉬운 작성법
    • Write 이후에 스케줄링 때문에 context switch가 발생할 위험이 없어지게 됨
  • 언제 스케줄링해야 할지 모른다?
    • Generl Purpose OS의 경우, 운영체제와 어플리케이션단을 분리
      • General Purpose OS는 Virus 때문에 무조건 선점을 해야 함
      • OS 주도 하에 스케줄링을 진행하기 때문에 해당 순서도 OS가 바꿀 수 있음
      • 본질은 Read와 Write를 함께 할 수 없을까에 관한 것
    • Embedded OS의 경우, 운영체제와 어플리케이션이 함께 작동
      • 어플리케이션이 동작이 끝나면 자신의 종료를 Signal을 통해 알리며 운영체제에게 스케줄링 타이밍을 알림(비선점형)
  • 본인의 서비스의 형태에 따라, Busy-waiting or Block/wake-up 방식을 채택할 수 있어야 함
    • 즉, 개발자는 본인의 서비스가 어떻게 작동하는지를 본인이 가장 잘 알아야 함
  • 대부분의 연산의 경우, Lock을 잡으면 반드시 unlock을 하고 싶어 함
    • Semaphore의 경우, V-P를 할 수 있음(Dining Philosophers 문제 참조)
  • Java의 synchronized, Python의 with와 같은 개념이 다 Monitor
    • 프로그래머의 실수를 줄이면서, 병행 제어의 역할 수행 가능
  • 프로세스(혹은 쓰레드)를 Module화 할 때는 서로 공유하는 부분이 최대한 적게 하는 것이 좋음

다음 주 까지의 assignment

  • 'Computer Networking: A Top-down approach' 정리
  • 이후 과제: UDP 소켓 프로그래밍으로 TCP 구현
  • 추가적으로 '후니의 쉽게 쓴 네트워크 프로그래밍' 읽어보면 좋음


두다지 서버 개발자 양성 프로그램 Week 5

5번 째 멘토링

운영체제 전반부 학습을 마치고, 학습한 내용에 대하여 멘토님과 이야기를 나누는 시간을 가졌다. 처음에는 (일명 '공룡책')을 기본서로 학습을 하려고 했으나, 모든 Chapter를 세세하게 다루고 있는 이론서를 혼자 공부하자니 크게 와닿지 않은 부분이 많아 KOCW에 올라와있는 반효경 교수님의 운영체제 강의를 통해 학습을 했다. 실무에서 운영체제가 어떻게 사용되고, 어떠한 문제가 발생할 수 있는지에 대한 교수님의 시기적절한 설명을 통해 운영체제를 공부하니 좋은 학습 결과를 얻을 수 있었다. 이후, 멘토님과 학습한 것을 정리하며 운영체제가 발전하게 된 배경에 대한 이해를 함께 얻을 수 있었다.

학습한 것에 대한 리뷰

  • 운영체제는 계속 변화하는 것이기 때문에 이론서의 내용은 맞는 것도 있고, 아닌 것도 있을 수 있음
    • 때문에 운영체제의 세세한 부분을 아는 것은 크게 중요치 않음
    • 자신의 도메인의 메인 운영체제(ex: Backend = Linux, Android = Android...)가 실제로 어떻게 동작하는지를 아는 것이 더 중요
    • 그러나, Linux는 모든 도메인에 사용되기 때문에 표준으로 알아두는 것이 좋음
  • PCB 내에 scheduling information에 따라, priority가 있을 수도 없을 수도 있음
    • ex) Roudn Robin의 경우, priority 있을 필요 없음

feedback

  • 개념 설명을 할 때 명료한 표현 사용
    • 내가 확신이 없으면, 듣는 이는 나에 대한 확신을 가질 수 없음
  • 개념 공부를 할 때는 zip -> 압축 해제 처럼 공부해야 함
    • 세세한 부분까지 모두 암기하는 것이 아니라 중요한 부분들을 모아 압축하는 식으로 공부
    • 3년 후에도 이전에 공부했던 내용 압축 해제 가능하도록!

Operation System

  • 운영체제는 Multi process를 제공하기 위해 발전
    • CPU Core를 1개 사용하던 시절, 사용자에게 여러 개의 Process가 동시에 돌아가고 있는 것처럼 느끼게 해주는 것
    • 그렇다면 Prcess는 무엇인가?
      • 실행 중인 프로그램(binary code)
  • CPU를 얻는 것은 일종의 '회의실 예약'과도 같은 개념
    • 선점/비선점의 문제
      1. 회의실 예약 시간을 정해놓고 정해진 시간이 되면 바로 사용 -> 선점
      2. 앞 팀의 사용이 모두 끝날 때 까지 기다렸다가 사용 -> 비선점
    • 우선 순위의 문제
      1. 직급에 따라 회의실 사용 -> 우선순위
      2. 직급에 상관 없이 회의실 사용 -> 비우선순위
  • 우선 순위를 사용하면 Starvation 현상이 발생할 수 있음
    • Aging 기법을 통해 해결 가능
  • General Purpose OS는 CPU 선점형이 기본
    • ex. Virus가 CPU 쥐고 안 놔주면 다른 프로세스들은 전혀 동작할 수 없게 됨
  • Windows XP는 Thread를 Type별로 나누어 다르게 처리
    • ex) UI thread는 우선순위 높여 CPU 빨리 주지만 빠르게 뺏음 -> 사용자 경험 증가
      • 사용자 경험을 증가시켜, 실제로는 작업 속도 빠르지 않지만 빠르다고 느끼게 하는 것
  • 스케줄링 알고리즘은 절대 복잡해서는 안 됨
    • 다음에 처리할 프로세스를 빠른 시간 안에 선택할 수 있어야 하기 때문
  • 프로세스 상태 등장 배경
    • CPU 할당에 혼돈을 제거하기 위해
      • 상태가 없다면 new, terminated 상태인 프로세스에게 CPU를 줄 수도 있음
    • 외부와 의사 결정을 하기 위해
      • readyblocked를 비교하여 사용!
        • 스케줄러는 blocked queue를 전혀 신경쓰지 않음
        • 스케줄링 되더라도 소용이 없기 때문
  • Why Thread?
    • Thread의 등장 배경
      • 대부분의 작업은 일을 나누어 수행하는 것이 좋음
      • 그러나, IPC 통하여 작업을 나누어 수행하는 것은 불편함
        • shared memory 지정해주고, init 해주고, 사용했던 garbage 값 지워주고...
      • 자원이 공유되는 프로세스 개념이 없을까?(esp. Memory) -> Heap 영역의 공유를 통한 Thread 등장
    • 스케줄링의 기본 단위는 Thread이기 때문에, 각각의 Thread들은 우선순위 정보도 따로 사용
    • but, 하나의 Thread가 죽으면 프로세스가 종료되기 때문에 다 같이 죽는 문제
      • 따라서 큰 서비스에서는 Multi-Thread로 프로그램을 짜는 것이 좋지 않음
      • Sharing을 위해서는 Redis와 같이 추상화된 Memory 영역을 사용
  • fork()로 프로세스를 생성할 때 부모/자식 관계를 만드는 이유?
    • 관리가 편해지기 때문
      • 부모가 자식 프로세스들을 프로세스 단위로 관리할 수 있게 됨
    • nohup: 자식 프로세스의 부모/자식 관계를 끊고 독립적인 프로세스로 변경(root만이 죽일 수 있게 됨)
      • 엄한 다른 부모 프로세스로부터 시그널을 받지 않게 하기 위함
      • 프로세스를 service로 사용해야 할 때 많이 사용

다음 주 까지의 assignment

  • 공룡책 Ch.10 까지 공부
    • Disk management는 SDD의 등장으로 중요성 감소
  • Spinlock의 실제 구현 이해하기
    • Linux에서 실제로 어떻게 구현되어 있는지?
    • Hardware에게 어떠한 도움을 받아 동작하는지?
  • Spinlock, Semaphores, Producer & Consumer problem 구현하기 in C lang
  • Thread의 join() 기능 구현해보기


두다지 서버 개발자 양성 프로그램 Week 4

4번 째 멘토링

한 주 간 컴퓨터구조론, Linux 그리고 Vim에 대한 학습을 마친 후, 학습한 지식에 대한 리뷰 시간을 가져보았다. 일주일 간 책을 기반으로 전산학의 기본 지식들을 학습하면서 내가 얻은(혹은 공부한) 지식에 대한 확신을 잘 얻지 못하였는데, 이번 시간 내가 학습한 것을 남에게 직접 설명하는 시간을 가져보며 학습한 지식을 내것으로 소화할 수 있었던 시간이었다. 단순히 책을 읽고 학습이 완료되었다고 생각하는 것이 아니라, 학습한 지식을 소화하여 내 것으로 만들 수 있는 시간을 충분히 가져갈 수 있어야 한다는 생각을 다시금 느낄 수 있었던 시간이었다.

학습한 것에 대한 리뷰

  • Vim에서 중요한 옵션
    • textwidth: 80-Columns Rule을 지키기 위한 옵션
    • expandtab: Tab이 아닌 4-spaces를 사용하기 위한 옵션
    • Syntax Highlight
  • 리눅스에서는 Quotation으로 원치 않는 확장 막아주는 것이 중요
    • 쉘 스크립트 작성에 중요한 요소이기 때문
    • Should know difference between Double Quotation and Single Quotation!
      • Double Quotation: 변수, `(backtick), escape 문자 등 특정 대상에 대한 확장 허용
      • Single Quotation: 어떠한 확장도 허용하지 않음
>> $ echo "$(echo "upg")"
upg
>> $ echo '$(echo "upg")'
$(echo "upg")
  • su는 다른 유저로 전환을 하기 위한 명령어
    • default 값이 Super user인 것이며, 인자를 넣어줌으로써 다른 User로의 전환이 가능

feedback

  • 전산학에 나오는 용어를 적재적소에 정확하게 사용하여 설명해야 함
    • 대명사 사용은 지양하는 것이 좋음(ex: 그거, 저거...)
    • 어떠한한 언어다.. 어떠한 프레임워크다.. 어떠한한 하드웨어 구성 요소이다.. 와 같이 정확한 끝말을 사용해야 함
  • 전산학의 경우, 자연과학과 달리 필요에 의하여 만들어진 것이기 때문에 왜 그러한 Concepts가 생겨난 것인지에 대한 이유를 잘 이해하고, 설명할 수 있어야 함
    • 어떤 배경에서 이러한 개념이 발생한 것인가?
    • 이전 기술의 한계, 소비자의 요구 등..
  • 이전 기술의 경우: 왜 그 당시 유행할 수 있었는지, 어떻게 다른 기술로 대체되어 왔는지에 대한 이해
  • 현재 기술의 경우: 현재 어느 분야에서 많이 사용되고 있는지 이해

Computer Architecture

Cache

  • Cache는 전산학에서는 저장장치이며, 현실 세계에서는 일종의 컨닝 페이퍼
    • Cache가 기능할 수 있는 이유는 Locality의 원리 때문
    • 결국, locality가 없는 곳에서는 cache를 사용하지 않아야 함
    • hit ratio가 0.9 이상이 되어야 사용하는 데 의미가 있음
      • hit ratio가 높다는 것은 locality가 크다는 의미!
  • Cache에서 가장 큰 문제는 데이터 불일치
    • 따라서 쓰기 정책을 잘 아는 것이 중요함
    • Write-through, Write-back...
  • 사상 정책: Cache를 어떤 구조로 가져갈 것인가?
    • 직접 사상은 O(1)로 해당 value을 찾아올 수 있지만, 해시 펑션에 의해 캐시 공간을 낭비할 가능성이 있음
    • 직접-연관 사상, 세트-연관 사상이 나오게 된 배경!
  • Web Cache: 이미지와 같은 경우 사이트에서 매번 가져오는 것이 비효율
    • 크기도 크고, 변할 가능성이 크지 않기 때문에
    • 따라서, 이미지에 대한 해당 url 요청을 Caching하여 정보 제공
      • 이 때는 Disk가 Cache의 역할 수행
      • Web으로부터 가져오는 것 보다는 Disk가 빠르기 때문에!
    • 그러나 해당 url 내 정보가 바뀐 경우는?
      • timeout 정책 적용을 통해 해결 가능. 일정 시간 지나면 cache된 url 정보 삭제
      • 혹은 브라우저의 캐시 기능을 해제하여 해결 가능 -> 개발할 때는 브라우저의 캐시 옵션 제거하고 하는 것이 좋음
  • CDN(Content Delivery Network): 콘텐츠를 효율적으로 전달하기 위해 여러 노드를 가진 네트워크에 데이터를 저장하여 제공하는 시스템
    • ex) 리니지 M이 업데이트 됨
    • NC Soft에 있는 전사 네트워크에는 기존 리니지 프로그램을 넣어 놓고, CDN에는 패치 프로그램 넣음

    • 새로운 콘텐츠(패치 프로그램)는 무조건 받을 수 밖에 없기 때문에 hit ratio(kinda)는 100%에 달하게 됨


Pipelining

  • 슈퍼파이프라이닝이란 가장 오래 걸리는 작업을 더 잘게 나누어서 수행하도록 하는 것
  • 각 CPU 코어가 자신의 일을 수행하지 못하고 idle 상태에 들어가는 것이 모두 Hazard
  • 파이프라인을 증축시키고자 하는 것이 슈퍼스칼라
  • 과거의 분기 예측 문제: 분기 되는 것이 common인가? 분기 되지 않는 것이 common인가?
    • ex) Error case에서의 분기문의 경우, 대부분의 경우 프로그램이 정상 작동할 것으로 가정하여 분기 되지 않는 것이 Common case로 설정
    • 현대에서는 이러한 최적화 문제가 잘 해결되어 있음
  • 4-line Pipeline이 가격 대비 성능이 좋기 때문에 사용되었던 것이지, 항상 4-line을 사용할 필요는 없음

System bus

  • 시스템 버스에서 가장 중요한 개념은 버스 중재
    • 복수의 버스 마스터가 동시에 Signal을 내면 다 같이 무너지기 때문!
  • Asynchronous I/O
    • Push:: 특정 event 발생하면 Push 알림
      • ex) 네이버 카페 키워드 알림
      • ex) 특정 프로세스가 DMA 기다리며 sleep 하는 경우
    • SOLID에서의 DIP(Dependency Inversion Principle)
      • ex) Hollywood 오디션의 예

        1000명의 오디션 지원자가 오디션
        지원자들의 합격 문의 전화로 인해 업무 마비
        전화 수신을 거부하고, 발표일에 일괄 발표하겠음!
        지원자 to 영화사에서 영화사 to 지원자로 의존성의 역전

    • Event Driven: Event가 발생하면 정보 제공
    • Observer Pattern: 등록한 Observer에게 event 발생하면 정보 제공
    • Semaphore: OS에서 다시 다룰 예정
  • Synchronous I/O
    • Polling: 궁금한 정보에 대해 계속 접근하는 것 -> 비싼 연산
    • Spinlock: OS에서 다시 다룰 예정
  • Asynchronous I/O의 단점
    • 읽어올 정보가 거의 항상 있을 경우
      • Context switch로 인해 1초에 1000개 정도의 정보가 유실될 가능성이 있음
      • 실제로 메이플스토리의 경우, 네트워크를 Non-blocking의 Synchronous I/O로 구축

다음 주 까지의 assignment

  • '공룡책(Operating System Concepts)' Chapter.05 까지 학습해오기
  • 2주에 걸쳐 운영체제 학습하는 시간 가질 것임!


두다지 서버 개발자 양성 프로그램 Week 3

3번 째 멘토링

지난 주 과제로 Test case를 포함한 해시 테이블과 우선순위 큐를 다시 직접 작성하였고, 작성한 우선순위 큐를 활용하여 간단한 스케줄러 토이 프로젝트를 만들어 보았다. 이 때, 기본적으로 작성한 우선순위 큐를 곧바로 사용하는 것이 아니라, 작업 스케줄러로 사용하기 위해 특정 method를 변환해서 사용해야 한다는 등의 생각을 하며 기본 자료구조의 활용을 고민해볼 수 있었다. 그리고 상속이 가진 문제점을 시작으로 하여, 상속의 문제점을 회피하되 그 장점은 살리기 위한 방법의 일환으로 나온 Strategy Pattern에 대한 이야기를 멘토님과 나누었다. 혼자 이해를 하려고 했을 때는 왜, 그리고 어떻게 해당 디자인 패턴이 현업에서 사용될 수 있을지에 대한 확실한 이해를 할 수 없었지만, 멘토님의 라이브 코딩을 보며 Strategy Pattern이 이처럼 효율적으로 사용될 수 있음을 느낄 수 있었던 귀중한 시간이었다.

Code Review

  • 현업에서의 코드 리뷰는 테스트 코드로 진행되는 것이 보통
  • 한 class는 400 line 정도로 작성하는 것이 좋음
    • 한 method는 40 line, 한 프로그램은 1000 line 이상 넘기지 않는게 좋음!
  • 화면 분할을 사용하는 프로그래머(ex. Vim user)들은 보통 한 파일에 몰아넣고 사용하는 경향이 있음
    • 반면에 파일을 쪼개서 작성하는 사람들도 있음. 즉, Case by case
  • Code convention 익힌 다음에는 안전하게 Editor 내 reformatting 기능 사용하는 것이 좋음
    • 익숙해지기 전까지는 연습하면서 공부!
  • python-fire(based on argparse) 등 잘 만들어진 module 사용
    • 핵심 Logic은 물론 본인이 구현해야 하지만, 다른 좋은 module을 같이 사용할 줄 아는 방법도 실력의 중요 요소
  • 대개 main thread는 UI로 하고, 이외의 기능들은 추가적으로 thread 생성해주어 사용하는게 보통

feedback

  • Class/method의 depth: prompt method 정도 길이로 작성하는 것이 딱 좋음
  • 공부 및 과제에 시간 투자 더 하기..

Why Interface?

  • 기계어로 모든 것을 작성하던 시절이 있었음...
    • 하나의 프로그램을 작성하는 것도 너무 어려움
  • 고수준의 C언어 등장. C가 가진 Struct(구조체) 개념
    • 사람의 생각과 직관을 실현시킬 수 있도록 만족시켜준 개념이자,
    • 서로 다른 크기의 메모리를 하나의 구조체에 담아 관리하는 것은 획기적인 개념
    • but, C언어 사용자들의 무분별한 goto문 분기로 인한 코드의 스파게티화가 문제가 됨
  • 함수로 가자!: 함수 단위로 packing 되어 내부에서 정돈되는 느낌
    • 프로그램은 사실 크게 보면 메모리와 메모리를 수정하려는 함수로 구성된 것임
    • 그러나, 함수 역시 모든 사람이 메모리를 접근하는 것에 대한 불만
  • Class 개념의 등장
    • 클래스의 특징: Class 내에 method와 멤버 변수가 존재
    • 기본적으로는 class 내에서 사용이 가능
      • 권한에 대한 문제: public, protected, private...
    • Heap에 형성된 메모리를 누구에게 사용 가능하게 할 것인지에 대한 강제성 및 규칙 부여 가능 -> encapsulation
    • 함수로 공동의 데이터를 고쳐나가는 것에서 Class로 자신에게 주어진 권한의 영역만을 사용하는 패러다임으로의 변화
  • 함수의 재사용: 상속을 통해 기존의 작성된 함수를 재사용해보면 어떨까?
    • ex) 개와 고양이의 '사료먹기'라는 행위

    Animal - Dog / Cat과 같이 작성하여 중복 메소드의 제거 가능

    • 상속이 없다면? 비슷한 작업을 하는데 계속해서 중복된 코드를 작성해야 함
    • 그러나, 상속이 남용됨에 따라 그 depth가 깊어지고 결과적으로 정신이 없어짐...
  • Interface의 등장으로 사람들은 상속을 멀리하기 시작함
    • 나에게 필요한 정보가 산재되어있는가?(Class) / 나에게 필요한 정보가 잘 정돈되어있는가?(Interface)
    • 실제로 Depth가 깊어진 상속 구조에서 method를 Override할 때, 하나의 오타라도 나면 해당 코드는 작동하지 않아야 하는데 작동되는 경우가 생김
    • 즉, 점점 구조적 어려움으로 오류를 찾기가 어려워짐
  • Interface라는 하나의 Summary note를 통해 주어진 기능들을 일목요연하게 보여주면 사용자의 이해를 돕고, 일종의 강제성도 부여할 수 있게 됨
    • 특정 행동에 대한 강제는 하지만, 그 내부적 구현은 해당 Interface를 사용하는 Class의 특성에 따라 다르게 설정할 수 있음
      • ex) Queue / Priority Queue / Circular Queue의 Interface: peek, pop, push의 methods
      • ex) Scheduler (Priority Scheduler / Round Robin Scheduler)의 Interface: add, delete, peek, pop의 methods
  • Interface를 자체적으로 지원 안하는 인터프리터 언어를 사용하는 개발자들도 Class를 Interface와 같이 사용하는 경우가 많아지고 있음
  • method 구현과 관련한 부분을 외부에서 넣어주는 것이 Strategy Pattern
    • 상황에 따라 바꾸어 사용할 수 있는 것이 Strategy!
    • 그러나 같은 method가 반복된다? 그냥 코드의 재사용으로 인정(아래 예제의 dummy_action 참조)
      • 상속으로 중복 method를 받아오게 되면 다시 그 method가 어느 부모 클래스에 속하는지 모르는 상속의 문제 발생
  • Java의 Decorator Pattern 역시 본질은 Interface의 구현과 관련된 문제
    • Interface를 어떻게 사용하는 것에 따라 달라지는 것이 Design Pattern
  • Strategy Patter의 예
def dummy_action():
    print('do a job')


class IEvent:
    def __init__(self):
        pass

    def run(self):
        assert False


class BackupPolicy(IEvent):
    def __init__(self):
        pass

    def run(self):
        dummy_action()


class ReportPolicy(IEvent):
    def __init__(self):
        pass

    def run(self):
        dummy_action()


class ISchedulePolicy:
    def __init__(self):
        pass

    def has_target(self):
        assert False

    def pop(self):
        assert False

    def add(self):
        assert False


class RoundRobinSchedulePolicy(ISchedulePolicy):
    def __init__(self):
        pass

    def has_target(self):
        # TODO
        pass

    def pop(self):
        # TODO
        pass

    def add(self):
        # TODO
        pass


class PriorityQueuePolicy(ISchedulePolicy):
    def __init__(self):
        pass

    def has_target(self):
        # TODO
        pass

    def pop(self):
        # TODO
        pass

    def add(self):
        # TODO
        pass


class Scheduler(object):
    def __init__(self, schedule_policy):
        self.schedule_policy = schedule_policy

    def loop(self):
        sp = self.schedule_policy
        while True:
            if sp.has_target():
                event = sp.pop()
                event.run()

    def add(self, event):
        sp = self.schedule_policy
        sp.add(event)


if __name__ == "__main__":
    # schedule_policy = RoundRobinSchedulePolicy()
    schedule_policy = PriorityQueuePolicy()
    Scheduler(schedule_policy).loop()



Computer Architecture - Pipeline

  • 메인 보드의 Clock에 맞추어 Task들이 동작
  • 파이프 라이닝은 일종의 컨베이어 벨트화
    • 각각의 독립된 Core에 To-do list를 전달
    • 하나의 Core는 주어진 하나의 일만 끝내고 쉬는 것이 아니라 부여받은 List의 일을 계속해서 수행
    • 이론적상으론 Core 갯수만큼의 성능 향상 얻을 수 있음
      • 마냥 행복할 수는 없음: Pipeline Hazard
  • 각 Core는 완료한 작업을 넘길 수 있는 매개체가 있어야 함(Memory, disk...)
  • Core에서 끝난 각 Task를 다음 Core에게 direct하게 넘기는게 아니라, Queue 구조를 사용하는 것이 일반적
    • Direct하게 전달할 시, 속도에 따른 불균형이 생길 수 있기 때문
  • 단순히 CA의 개념에서 그치는 것이 아니라 실제로 Scrapy와 같은 소프트웨어에서도 파이프라이닝 사용
    • 기본 개념을 알면 실생활에서 '이렇게도 사용할 수 있지 않을까?'라는 생각을 할 수 있게 됨

다음 주 까지의 assignment

  • Cache, Pipeline, I/O interrupt, 시스템 버스 학습해오기 from <컴퓨터구조론>
  • <리눅스 커맨드 라인 완벽 입문서> 읽고, 중요 개념 정리하기
  • vim 익히기
    • Tool에 익숙해야 내 실력을 온전히 발휘할 수 있음


첫 번째 팀 프로젝트를 마친 후 느낀 점

빅데이터 전문가 과정의 마지막으로 팀 프로젝트를 진행했다. 처음으로 진행한 프로젝트가 개인 프로젝트도 아닌 팀 프로젝트이다 보니 느낀 부분이 너무나도 많아 따로 정리를 하고 넘어가야 겠다는 생각을 하게 되었다.


내가 사용하는 Framework와 관련한 기본기의 중요성

  • Python 문법만 안 채, 다짜고짜 Django Framework 사용
    • 당연히 모르는 것 투성. 구조적 특징조차 이해하지 않고 시작한게 화근
    • Framework에 대해 기본적 공부라도 하고 진행했으면 삽질하는 시간이 대폭 감소했을 것
  • Spring을 배울 때도 JSP와 Servlet의 기본 지식을 알면 도움이 많이 될 것 같다는 생각을 하게 됨

배포와 네트워크의 어려움

  • 클라우드 서버 사용의 어려움을 뼈저리게 느낌
  • 포트포워딩과 같은 기본적 네트워크 지식을 단단히 갖추어야 겠다는 생각
    • 공인 IP vs. 사설 IP
      • ex) 내가 사설 IP에서 80번 포트를 열면 상대는 '공인IP:20682'와 같은 포트를 통해 들어오는것임
    • 단순히 인바운드 Port만을 열어주는 것이 아니라 해당 Port의 방화벽도 풀어주어야 함
    • 다양한 배포의 장이 있구나..(pythonanywhere, KT U-cloud biz, toast cloud, Azure, AWS...)

의존성

  • python2와 연동할 수 있는 Django version...(어차피 deprecated)
  • Python3과 연동할 수 있는 Django version
    • 3.x의 작은 버전 차이로 Django의 2.x에 의존성 문제 생김
    • ex) Python 3.4는 Django 2.0 사용 / Django 2.1 부터는 python 3.5 이상의 버전을 요구
    • Spring boot는 설정값이 스스로 의존성을 관리해준다는데...
  • Python2와 Python3이 모두 깔려있을 때, pip와 pip3 각각이 설치할 수 있는 패키지의 차이 존재ㄴ
  • Version up은 링크 설정으로 해결하자!

분담의 위험성

  • 지나친 분담은 개인의 프로젝트에 대한 전반적 이해도를 떨어뜨리는 결과를 낳게 됨
  • 적절한 양으로의 분담과 주기적인 의견 교환 자리를 만들어 서로가 수행하고 있는 파트를 이해해야 함

그럼에도 불구하고..

  • REST API에 대한 느낌적 이해
    • 카카오 플러스 친구를 생성하기 위해 Kakao가 제시하는 API를 사용해보며 RESTful 하다는 것이 무엇인지 느낌적으로 경험
    • REST API에 대한 개념적 이해는 추가적으로 공부해서 얻도록!
  • 기본기를 튼튼하게 하자는 다짐
  • 삐그덕거렸음에도 분담을 했던 팀원들의 출중한 실력으로 입상


두다지 서버 개발자 양성 프로그램 Week2

2번 째 멘토링

한 주 간 과제로 공부하고 작성한 Priority Queue와 Hash Table을 리뷰하는 시간을 가졌다. 멘티들이 공부해온 자료구조의 특성과 구조를 멘토님께 설명한 후, 작성한 코드가 어떻게 작동하는지에 대하여 이야기하였다. 이후에는 작성한 코드에 대한 리뷰와 자료구조라는 과목이 왜 중요한지 그리고 어떻게 사용되는지에 대한 추가적 설명을 듣는 시간을 가졌다. 자료구조를 학습하며 전산학에서 중요한 과목이라는 말을 수도 없이 들었지만 정작 왜 그리고 어떻게 사용되는지에 대해 깊게 고민해본 적이 없었는데 멘토님의 설명을 들으며 자료구조의 중요성에 대해 이해할 수 있었던 시간이었다.

Code Review

Coding Convention

  • method name은 항상 underscore와 lowercase로 구성되어야 함
    • ex) def perc_up:
  • method name을 구성하는 영단어는 full로 사용하는 것이 이해하기에 좋음
    • ex) def percolate_up:
  • class name은 Camel case로 작성
    • ex) class PriorityQueue:
  • underscore + capitalize 조합만큼은 피하자
    • ex) class Priority_Queue (ugly!)
  • Tab과 Space의 혼용은 가장 **'지양'**해야 할 Coding Convention
    • ex) 소스 코드를 merge하고자 하는데 충돌이 일어남. 그러나 소스 코드 전체를 뒤엎고 있는
      Tab/Spaces의 indentation 충돌로 인해 정작 중요한 충돌 지점을 찾지 못하는 경우가 생길 수 있음
    • show whitespace option + use 4 spaces as an indentaion option

Feedback

  • Data Structure의 Basic concepts 익힌 후에는 직접 구현해보는 것이 가장 좋음
  • +) Test case를 구상해가며 코드 작성과 Test를 함께 하는 습관을 들이는 것이 좋음
    • Performance 까지 측정해보면 better!
  • 3개월 후가 중요하다!
    • 3개월 뒤에 코드를 읽었을 때 짧은 시간 안에 작성했던 context를 이해할 수 있도록 작성하는 것이 중요

Data Structure Review

Priority Queue

  • 다른 많은 구조적 장점을 포기하고, Find min(or max)를 Constant time(O(1))에 하기 좋은 자료구조
    • Heap을 사용한다면 삽입과 삭제는 O(logN)의 시간 복잡도를 지니게 됨
    • 따라서 Heap을 이용한 Prioirty Queue의 구현을 위해서는 Heap의 특성을 유지하도록 하는 'heapify' 연산이 가장 중요
  • 우선순위 큐가 실제 전산학에 사용되는 예
    • OS의 Job Scheduling
      • 다음 작업의 Scheduling을 위해서는 가장 상단의 Job만 참조하면 되기 때문에 모든 Job들의 우선순위를 확인할 필요(Full Scan)가 없어짐
    • Network traffic control

Hash Table

  • Hash Table의 size는 Prime number로 정의하는 것이 Open slot(Open addressing)을 찾는데 효율적
  • Hash Table의 가장 중요한 척도는 Load Balance
    • 실제 Table의 index를 우리가 원하는 key로 접근하지 못할 수도 있음
      • ex) a[99]가 아닌 a['사과']로 검색하고 싶다면?
    • Hash Function은 단순하되 Key값을 정해진 Table의 slot에 올바르게 mapping하는 것이 중요
    • Collison로 인해 생기는 Clustering은 Load balance를 망침
    • Sharding: 데이터가 아닌 데이터베이스 자체를 분할하여 분산 저장(CRUD 연산 성능 향상 가능)
  • Hash Table의 size를 resizing 할 때는 기존에 들어가 있는 값들의 위치에 대한 고려를 해야 함
    • 번거롭더라도 기존에 저장된 값들을 모두 꺼낸 후, resizing을 위해 새로 작성한 hash function에 맞게 값을 저장하는 것이 좋음
  • 전산학에서는 O(1)과 O(logN)의 시간 복잡도를 'elegant' 하다고 여김
    • 단순한 doubling 기법이 효율적인 이유

Conclusion

  • 내가 원하는 Common case의 속도 최적화를 위해 다른 부분을 포기하는 것이 중요할 때가 많음
    • 이것이 Data Structure를 공부하는 이유(어떤 자료구조가 어떤 연산에 특화되었는지를 이해할 수 있어야 하므로)
    • 전산학 자체가 모두 이같은 최적의 개념으로부터 시작
      • A를 위해 B를 포기하거나, A와 B 모두 중간 단계의 속도로 맞추거나 등...
  • 즉, Common case fast를 중요한 모토로 삼아야 함
    • 서비스의 Common case를 찾는 것 또한 중요한 과정
    • Common case를 발견한 후, 이를 위해 어떤 구조체를 사용할 것인지까지 결정하고 나면 나머지 작업 역시 해결되는 경향이 강함
    • 실생활에서 이와 같이 코드를 작성해야 할 일이 정말 많을 것
  • Main Language에서 제공하는 기본 Data Structure가 내부적으로 어떻게 작동하는지를 아는 것 또한 중요함

다음 주 까지 Assignment

  1. Test case를 포함하여 Priority Queue와 Hash Table 재구현 및 테스트
  2. 우선순위 Queue를 이용한 Job Scheduler 작성
    • [요구사항]
      1. CLI 혹은 Web으로 서비스 구현
      2. add 21:32 'process name'와 같이 작업 추가
      3. Queue에 들어있는 명령어 보여주는 listing 명령어 추가
      4. 2개의 thread 사용(시간 체크하는 thread / 명령어 받고 해석하는 shell thread)
  3. 김종현 교수 <컴퓨터구조론> 읽고, 중요 내용 요약
  4. 상속과 인터페이스의 차이 이해하기
    • 상속의 단점: 왜 Go lang은 '상속'을 버렸을까?
    • Strategy pattern의 이해


두다지 서버 개발자 양성 프로그램 Week 1


두다지에서 진행하는 서버 개발자 양성 프로그램 3기에 참여하게 되었다. 시간과 돈을 투자하여 좋은 멘토님에게 수업을 듣는 만큼 멘토님이 말씀하신 내용들을 기록하여 차후에 다시 학습을 함에 있어 내 자신을 remind 시키는데 도움이 되게끔 기록을 남긴다.


간단한 정렬 알고리즘의 구현과 코드 리뷰

멘토와 멘티들의 간단한 자기 소개 이후, 멘토님은 우리에게 Warming up 삼아 본인이 작성할 수 있는 정렬 알고리즘의 코드를 작성해보도록 시키셨다. 작성 언어의 경우, Python으로 통일. 평소 알고리즘에 큰 자신이 없는데다가, 누군가의 앞에서 코드를 작성해본 경험이 없었던 나는 알고리즘을 작성한 그 짧은 시간이 너무나도 길게 느껴졌다... Quick Sort 혹은 Merge Sort와 같이 내가 알고 있는 효율적 정렬 알고리즘을 구현해보고 싶었으나, 당장 Pseudo code를 보지 않고 작성할 수 있는 Bubble Sort와 Selection Sort를 작성하기로 하였고, 코드 작성 이후에는 멘토님의 코드 리뷰가 이어졌다.


버블정렬과 선택정렬의 구현

  1. Bubble Sort
a = [3,4,1,2,7,9,10]
size = len(a)

for j in range(size-1, 1, -1):
	for i in range(0, j):
		if a[i] > a[i+1]:
			tmp = a[i]
			a[i] = a[i+1]
			a[i+1] = tmp

  1. Selection Sort
b = [3,4,1,2,7,9,10]
size_b = len(b)

for i in range(0, size_b-1):
	tmp_min = b[-1]
	idx = -1

	for j in range(i, size_b-1):
		if b[j] < tmp_min:
			tmp_min = b[j]
			idx = j

	b[i], b[idx] = b[idx], b[i]

코드 리뷰

  1. 스타일의 준수
    • Python이 공식적으로 내놓은 스타일 가이드인 'PEP 8'를 준수하는 것이 가장 좋음
    • 80 Columns Rule: 한 Line에 80개 이상의 column이 들어가지 않도록 하는 것이 좋음
      • 한 화면에서 라인이 모두 보이는 것이 가장 이상적이기 때문
      • 위아래도 마찬가지로. 위아래가 너무 길어지면 Flattening 작업을 해주는 것이 바람직
      • 긴 문자열을 사용해야 하는 경우, 이례적 예외가 있을 수 있음
  2. Logic을 module화 하여 사용하는 습관 들이기
    • 실제 기술 면접과 같이 Interviewer 앞에서 손코드를 작성하는 자리가 아닌 이상, 위와 같은 코드는 가독성이 떨어짐
    • 코드의 Logic을 module화 하게 되면 가독성이 좋아질 뿐더러, 80 Columns Rule의 준수도 가능해짐
    • Module화 한 method들을 import하여 한 번에 같이 사용
    • Module화 하여 사용할 경우 적절한 'Exception Handilng'에 대한 고민도 같이 하게 됨
  3. static한 작성이 아닌 적절한 Unit test를 통한 코드의 작성
    • 반드시 완벽한 로직을 생각하고 난 후에 코드를 작성할 필요는 없음
    • assert와 pytest를 잘 활용하여 작성한 method가 올바르게 작동하는지 등의 Unit Test를 수행하며 작성하는 것이 좋은 접근 방법
    • +) 2번에서의 Module화는 Method 단위의 Test 또한 가능하게 해주는 장점이 있음
  4. Function(Method) Call과 관련한 성능 관련 이슈
# 좋은 예
count = len(array)
while i < count:
	~~~~~~~~~~~

# 안 좋은 예
while i < len(array):
	~~~~~~~~~~~~~~

위의 두 코드 중, 좌측의 코드가 변수 메모리를 하나 더 사용함에도 불구하고 성능면에서 더 효율적일 수 있음. 오른쪽 예의 코드와 같이 len() 함수를 반복적으로 사용하는 경우를 피할 수 있게 해주기 때문.


변수의 선언을 메모리의 낭비라고 생각하지 말고, 위의 예에서와 같이 자주 사용될 것 같은 값은 변수로 미리 만들어 재사용하는 것이 더 효율적


그렇다면 위의 내가 작성한 Selection Sort에서 최소 값 원소의 index를 찾는 안쪽 for문의 Logic을 method화 하면 어떨까?

# 기존에 작성한 코드

for i in range(0, size_b-1):
	tmp_min = b[-1]
	idx = -1

	for j in range(i, size_b-1):
		if b[j] < tmp_min:
			tmp_min = b[j]
			idx = j

	b[i], b[idx] = b[idx], b[i]

# method화 하여 호출한 코드

for i in range(0, size_b-1):
	idx = find_min_idx(b[i:])

	b[i], b[idx] = b[idx], b[i]

내가 작성한 코드의 경우, 함수 호출 없이 한 Logic 안에서 모든 연산이 이루어진다. 그러나, 두 번째와 같이 해당 영역 Logic을 method로 만들어 호출하게 되면 Stack 메모리에 분기(Jump) 명령어가 추가적으로 실행되어 값을 얻을 수 있게 된다. Heap 영역 연산에 비해 Stack 영역에서의 연산이 보다 더 저렴한 연산이라 할 지라도 이는 분명 성능면에서 손해를 가져올 수 있다.


그럼에도 아래와 같은 코드를 사용하는 것이 더 바람직하다고 할 수 있다. 그 이유는 method를 재호출하여 얻는 '코드의 가독성'이 method의 호출로 인해 생기는 '성능면의 손해'를 상쇄시키고도 남기 때문이다. 따라서 이렇다 할 큰 성능의 차이를 보이지 않을 것이라고 생각되는 Logic의 경우 멘토님이 말씀하신 바와 같이 아래의 코드 작성법을 사용하여 코드의 가독성을 택하는 것이 바람직하다.


cf. C언어 계열에서의 inline 함수가 존재하는 것도 이러한 이유에서 비롯된다. inline 함수를 사용하게 되면 가독성과 성능이라는 두 마리 토끼를 다 얻을 수 있다. 그러나 과도한 inline 함수의 사용은 Overhead의 증가로 이어지기 때문에 적절하게 사용하는 것이 중요


Learn Practical things

  1. vim
    • 멘토링 진행 전, Ubuntu를 설치해오라고 한 이유의 한 부분을 차지할 정도로 강력한 텍스트 편집기
    • 개발을 함에 있어 shortcut과 command만 잘 익히고 있으면, 마우스 사용으로 인한 시간과 동선의 낭비를 획기적으로 줄일 수 있음
    • 특별히 선호하지 않는 에디터가 있지 않는 이상 VS Code에 vim plug-in을 설치해서 사용하는 것이 괜찮은 선택
  2. git
    • 협업이 중시되는 전산직종에서 git을 다루는 것은 필수적
    • 능통하게 다룰 수준은 아니더라도, 기본적인 사용법은 모두 숙지하고 있어야 함
    • 생활코딩의 강좌 '지옥에서 온 git' 를 통해 학습하는 것을 추천
  3. Linux
    • 2000년대 초반까지만 해도 윈도우 기반의 서버와 리눅스 기반의 서버 모두 사용되었으나, 이제 99% 이상의 서버가 리눅스에서 작동
    • 클라이언트 개발자가 된다 하더라도 개발자가 리눅스 CLI(Terminal)를 접하지 않을 일은 전혀 없음
    • 따라서 Linux terminal 환경에서 코드를 작성해보고, 서버를 띄워보는 등의 경험을 하는 것은 필수적

프로그래밍 언어에 대한 멘토님의 고견

  • 언어 선택에 큰 고민을 하는 것은 효율적 고민이 아닐 수 있음. 잘 나가는 회사는 사용할 언어에 크게 제약을 두지 않음
  • 지금 유행하는 언어와 플랫폼이 언제 시장에서 도태될지 모르는 곳이 IT 시장. 따라서 우리는 변화하는 환경에서도 지속적으로 사용할 수 있는 지식을 갖추어야 하는데 그것이 바로 전산학과 관련된 기본 지식
  • 전산학에서 배우는 Computer Architecture, Data Structure, Algorithm 등은 10년 전에도 중요한 것이었고, 향후 10년 간도 중요할 것
  • 따라서, 전산학에 대한 기본기를 튼튼하게 갖추는 것을 최우선 목표로 삼는 것이 좋음. 전산학에 대한 이해도가 좋은 사람은 새로운 언어를 습득하는데에 있어 짧게는 6개월 길면 1년 정도의 시간만 투자하면 해당 언어로 원하는 프로그램을 작성할 수 있는 수준에 이를 수 있음. 즉, 전산학을 통해 배움에 대한 가성비를 갖출 수 있음
    • 멘토님의 Angular 이야기를 들으며 백기선님이 학습법과 관련하여 하신 이야기가 생각이 났다. 기본기를 갖추고, 두루두루 잘 할 수 있는 개발자가 되도록 노력하자!

  • 그럼 우리는 어떻게 해야할 것인가?
    1. 내가 사용(해야)하는 Main language와 생산성을 위한 Script language 최소 2개 언어를 다룰 수 있어야 함
      • 나의 경우, Data Engineering에 큰 부분을 차지하는 Hadoop eco-system이 모두 Java로 작성되어 있기 때문에 Java를 Main language로, 내가 생각한 것들을 바로 구현해볼 수 있는 생산성을 위한 Script language로는 Python을 가져가고자 함. 차후에는 Scala도 공부해보고 싶다.
    2. 두 언어를 사용하여 내가 원하는 서비스를 웹으로 구현(웹 사이트, 게시판 등)할 수 있을 정도로 웹 관련 지식도 갖추어야 함

다음 주 까지의 Assignment

  1. Hash Table 구현
  2. Priority Queue 구현


  • 우리가 아직까지 자료구조를 공부해야 하는 이유?
    • 우리가 구현할 서비스에 따라 Data를 대상으로 하는 연산의 종류가 다를 수 있음
      • 어떠한 서비스는 데이터의 조회를, 또 어떠한 서비스는 데이터의 추가를...
    • 따라서 해당 연산에 강점을 가지는 적합한 자료구조를 사용하는 것이 성능과 비용면에서 좋은 선택
    • 완벽한 Data Structure는 없다. 그러나 나의 서비스 환경에서의 최적 Data Structure는 존재한다.
  • 자료구조 교재 하나 택해서 다시 처음부터 끝까지 복습하기(교재는 크게 중요하지 않음)
  • 단순히 해당 자료구조를 구현해보고 그치는 것이 아니라 Python에 내장되어 있는 List, Dictionary 자료형과의 추가, 검색, 삭제 등의 연산 시간을 비교해보며, 왜 수행속도 면에 있어 차이가 있을까. 그 내부 동작에 대한 고민을 깊게 해보는 것이 나중에 큰 자산이 될 것


python useful list method

  1. append(): 1개의 element를 추가하는 기능
words = []
for w in 'apple':
    words.append(w)

print(words) // ['a','p','p','l','e'] 출력

words2 = [w for w in 'apple']

print(words2) // ['a','p','p','l','e'] 출력

  1. extend(): 다수의 elements를 추가하는 기능
x = [1, 2, 3]
x.extend([4,5])

print(x) // [1, 2, 3, 4, 5] 출력

  1. insert(): 해당 index에 element를 추가하는 기능
x = [1, 2, 3]
x.insert(0, 4)

print(x) // [4, 1, 2, 3] 출력

  1. remove(): list에서 첫 번째로 나오는 element 삭제
x = [1, 2, 3, 4, 5]
x.remove(4)

print(x) // [1, 2, 3, 5] 출력

  1. index(): 해당 element가 어느 index에 존재하는지 확인하는 기능
x = [1, 2, 3, 4, 5]
x.index(5)

// 5가 4번 째 index에 위치하므로, 4 출력

library용과 execute용으로 사용할 수 있는 파이썬

def add(a, b):
	return a + b

class Calc:
	def execute(self, r):
			return r * 2

위와 같이 사용하면 library용으로 사용해야 하므로 반드시 import해야 함

if __name__ == "__main__":
	print("Execute")
	a = Calc()
	print(a.execute(4))

그러나 위와 같이 entry point를 생성해주면 곧 바로 실행하여 사용 가능


python에서의 예외 처리

try:
    f = open("testPython.txt", "r")
    f.write("Test....")
except IOError:
    print("Error...")
else:
    print("Nothing")
  • except 구문을 통해 다양한 예외 처리와 그에 대한 에러 문구를 출력할 수 있음. 위 예제의 경우, 해당 text 파일이 있을 시 파일 오픈과 write이 수행되고, 에러 예외에 걸리지 않았다는 "Nothing" 문구가 출력
  • IOError 외에도 ValueError, NameError 등 다양한 에러가 존재

Python 상속과 Overriding

  • Class의 상속을 위해서는 자식 Class에 Parameter로 부모 Class를 넣어줌
  • 다른 OOP 언어와 마찬가지로 method overriding을 위해서는 자식 class에서 부모 class 내 method와 같은 이름의 method를 재정의 해주면 됨

Python etc..

  • ./test.py 와 같이 python 명령어 없이 바로 코드를 실행하기 위해서는 소스 코드에 #!/usr/bin/python과 같이 python이 설치된 경로를 명시해주어야 함


  • method에 가변 변수 설정 가능

  • class 내 변수 앞에 __ 을 붙이면 inforamtion hiding 기능(Pseudo)

  • sys.path.insert(0, 'python 설치 경로') 로 환경변수의 추가 가능



+ Recent posts