컴파일러

  • 전체 소스코드를 보고 명령어 수집하고 재구성
  • 고레벨 언어를 바로 기계어로 변환

 

인터프리터

  • 소스코드의 각 행을 연속적으로 분석하며 실행
  • 고레벨 언어를 바로 기계어로 번역하지 않고 중간 코드(바이트 코드)로 변환시킨 후 기계어로 변환

 

인터프리터의 특징 4가지

  1. 인터프리터는 고레벨 언어를 중간 코드로 변환하고 이를 각 행마다 실행한다.

  2. 컴파일러가 각 행마다 실행하는 특성을 가진 인터프리터보다 실행시간이 더 빠르다.

  3. 컴파일러는 전체 소스코드 변환 후 에러를 보고하지만 인터프리터는 각 행마다 실행하는 도중
    에러가 보고되면 이후 작성된 코드를 보지 않는다. => 보안적인 관점에서 도움이 된다.

  4. 파이썬은 인터프리터 언어, C,C++은 컴파일 언어, 자바는 컴파일러와 인터프리터 모두 사용한다.

 

Compiler와 Interpreter가 하는 일 ( 자바 기준 )

컴파일러가 하는 일은?

 

helloworld를 작성하고 javac 명령어를 통해 helloworld.java 파일을 helloworld.class로 변환하는 것을 수행한다.

helloworld.java -> (javac) -> helloworld.class

 

응? 컴파일러는 기계어로 변환하는 프로그램이라고 했는데 .class 파일은 바이트 코드인데 이게 기계어인가...?
컴파일러는 소스코드 -> 오브젝트코드, 고레벨 언어 -> 저레벨 언어인 기계어로 변환한다. 
여기서의 기계는 하드웨어가 아닌 JVM을 위한 기계어로 변환한다는 뜻이다.

 

 

인터프리터가 하는 일은?

자바 컴파일러에 의해 변환된 .class 파일 내의 바이트 코드를 특정 환경의 기계에서 실행될 수 있도록 변환한다.

 

 

여기서 Machine language는 JVM을 말한다.

 

 

자바는 왜 컴파일과 인터프리터를 병행할까?

바로 기계어로 변환시키는 컴파일러는 프로그램이 작성된 기계상에서 실행할 때 매우 효율적이다. 대부분의 하드웨어
제어 시스템의 프로그래밍 언어가 C언어인 이유이다. 하지만 그렇기 때문에 기계의 종류에 종속될 수밖에 없다.

 

C언어와 달리 자바는 인터프리팅과 컴파일을 둘 다 사용한다.

인터프리팅은 플랫폼에 종속되지 않는다.

자바 바이트 코드는 컴퓨터와 프로그램 사이에 별도의 버퍼 역할을 한다.
    ☞ 이는 보안적인 장점이 될 수 있다.

 

이로 인해 자바는 플랫폼에 독립적이고 안전한 환경을 제공하면서 동시에 현대 프로그래밍 추상화를 완벽히 
수용할 수 있다.

 

즉, 자바는 컴파일러로 바이트 코드를 생성해 보안적인 장점을 얻음와 동시에 인터프리터를 사용해 플랫폼에
독립적이다.

 

컴퓨터는 0과 1로만 이루어진 기계어만 이해할 수 있기 때문에 개발자가 만든 코드를
변환해 주어야 한다. 이 역할을 하는 것이 컴파일러이다.

 

[Java] 컴파일 과정

자바는 OS에 독립적인 특징을 가지고 있다. 그게 가능한 이유는 JVM(Java Virtual Machine) 덕분이다.

 

자바 컴파일 순서

1. 개발자가 자바 소스코드(.java)를 작성한다.

 

2. 자바 컴파일러(Javac)가 자바 소스파일을 컴파일한다. 이 때 나오는 파일은 자바 바이트 코드(.class) 파일로
    아직 컴퓨터가 읽을 수 없고 JVM이 이해할 수 있는 코드이다.
    ( 바이트 코드의 각 명령어는 1바이트 크기의 Opcode와 추가 피연산자로 이루어져 있다. )

 

3. 컴파일된 바이트 코드를 JVM의 클래스로더(Class Loader)에게 전달한다.

 

4. 클래스 로더는 동적로딩(Dynamic Loading)을 통해 필요한 클래스들을 로딩 및 링크하여
   런타임 데이터 영역(Runtime Data area), 즉 JVM의 메모리에 올린다.

   ※ 클래스 로더 세부 동작
      ① 로드 : 클래스 파일을 가져와서 JVM의 메모리에 로드
      ① 검증 : 자바 언어 명세(Java Language Specification) 및 JVM 명세에 명시된 대로 구성되어 있는지 검사
      ① 준비 : 클래스가 필요로 하는 메모리를 할당 ( 필드, 메서드, 인터페이스 등등 )
      ① 분석 : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
      ① 초기화 : 클래스 변수들을 적절한 값으로 초기화 ( static 필드 )

5. 실행엔진(Execution Engine)은 JVM 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행한다.
   
   이때 실행 엔진은 두 가지 방식으로 변경한다.
   ① Interpreter
        - 바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다. 하나하나의 번역속도는 빠르나,
          전체적인 실행 속도는 느린 단점이 있다.


   ② JIT Compiler ( Just-In-Time Compiler )
        - 인터프리터의 단점을 보완하기 위해 도입된 방식
        - 바이트 코드 전체를 컴파일하여 바이너리 코드로 변경하고 이후에는 해당 메서드를 더이상 인터프리팅 하지
          않고, 바이너리 코드로 직접 실행하는 방식
        - 하나씩 인터프리팅하여 실행하는 것이 아닌 바이트 코드 전체가 컴파일된 바이너리 코드를 실행하는 것이기
          때문에 전체적인 실행속도는 인터프리팅 방식보다 빠르다.
    
    자바는 javac로 컴파일하고 java로 실행 시 바이트 언어를 한 줄씩 자바 인터프리터가 번역하기에 컴파일 언어이면서
    인터프리터 언어이다.

 

[C++] 컴파일 과정

GCC의 컴파일 과정

  1.  전처리기가 cpp 파일과 header파일을 읽어와 인라인시키고 전처리된 .i 파일로 만든다.

  2.  컴파일러는 이 .i 파일을 컴파일해서 기계어와 가장 유사한 상태인 어셈블리어로 변환된 .s 파일을 생성한다.

  3.  어셈블러는 .s파일을 어셈블해서 2진수로 이루어진 기계어로 된 .o 파일을 생성한다.

  4.  링커는 만들어진 .o 파일을 실행 파일로 변환한다. 링크 과정에서 각 .o 파일의 관계가 서로 연결되고
     필요로 하는 라이브러리도 함께 코드화되어 실행 가능한 파일이 만들어진다.

 

Java보다 C,C++의 수행속도가 빠른 이유

일괄 컴파일 방식 언어인 C,C++의 코드는 컴파일만 하면 바로 CPU에서 실행이 가능한 코드인 기계어로 변환된다.
그렇기 때문에 수행속도가 빠르다.

 

Java의 코드는 컴파일을 해서 byte code를 생성하고, 그 byte code를 기계어로 변환하는 시간을 필요로 하기 때문에
수행 속도가 더 오래 걸린다. 

 

게임 서버에서 가장 중요한 것

  • 안정성
  • 성능

성능을 높이려면?

  • 프로그램 최적화
  • 멀티코어 활용

멀티코어를 활용하려면

  • 멀티쓰레드 프로그래밍이 필요

온라인 게임을 만들려면?

  • 소켓 프로그래밍 필요

다중 접속 서버를 만들려면?

  • 서버에서 동접과 같은 수의 소켓을 관리하여야 한다

효율적인 다중 접속 관리는?

  • IOCP가 필수  -> IOCP는 멀티쓰레드 프로그래밍을 요구한다.

 

프로세스와 쓰레드

프로세스 :  실행 중인 프로그램

쓰레드    :  프로그램(프로세스) 실행의 흐름 -> 프로세스 실행 중 프로그램이 쓰레드 생성 명령 실행 

 

 

프로세스는 실행 중인 프로그램을 프로세스라고 한다. 스레드는 프로세스의 안에 있는 것이다. 멀티스레드 프로그래밍을
하지 않아도 스레드 1개는 있다(메인부터 해서 끝날때까지의 흐름). 

 

병렬처리

  • 하나의 작업을 여러 개의 context에서 수행하는 것( context == CPU의 실행 상태 )

하나의 프로그램이 병렬로 실행된다. 그 단위를 context라고 정의한다. context가 여러개 있다는 것은
여러 개의 cpu가 동시에 실행한다는 뜻이다. cpu의 실행 상태는 context이고, cpu가 한 개 있을 때 여러 개의
프로세스를 실행한다는 것은 하나의 cpu가 옮겨가면서 프로세스들을 실행한다는 뜻이다. 그렇기 때문에
실행 상태를 저장해야 하고 그걸 context라고 한다. 이 저장은 PCB에 저장된다. 프로세스에 여러개의 context가
있는 게 병렬 컴퓨터이다.

 

병렬처리를 하는 이유는?

  • 한 개의 CPU의 처리 속도가 너무 느리기 때문

예전에는 1G CPU 팔다가 2G CPU 팔면서 2배 빠른 컴퓨터를 사라고 홍보했지만 이제는 안된다. 지금 나오는 CPU
클럭은 3G 정도다. 2002년에 CPU가 2.4G가 있었으니까 지금 CPU와 클럭 속도의 차이가 얼마 안 난다. 그렇기 때문에
싱글코어 CPU의 성능 차이는 거의 없다. 그러면 CPU 만드는 회사들이 망할까? 아니다. 그래서 나온게 바로 멀티코어다.
싱글 코어 팔다가 듀얼 코어 팔면서 2배 빠르다고, 그리고 쿼드코어 팔면서 4배 빠르다고, 이제는 코어 개수로 승부한다.

 

프로세스

프로세스는 PCB라는 자료구조가 있다. 이건 커널이 관리하는 자료구조로 우리가 읽고 쓸 수 없는데, 여기엔 프로세스
실행에 필요한 여러 데이터가 있는데 그 중 context가 있다. 스택 포인터(SP)가 어디까지 와 있는가는 레지스터에 있고,
지금 코드에 어느 명령어가 실행 되는가는 프로그램 카운터(PC)에 들어 있다.

 

그래서 실행 중인 프로세스는 context가 CPU 안에 들어 있다. CPU 안에 있는 레지스터가 쭉 사용되다가 프로그램이
스위칭 되면 CPU 안에 있는 레지스터 내용을 context에 저장하고 다른 프로세스를 실행한다.

 

 

멀티스레드

  • 하나의 프로그램의 여러 곳이 동시 다발적으로 실행되는 프로그래밍 기법
  • 최근에 가장 많이 사용되는 병렬처리 프로그래밍 기법
  • 하나의 프로세스에서 여러개의 실행 흐름을 만들어주는 것
  • 하나의 쓰레드는 한번에 하나의 일만 할 수 있다. 멀티쓰레드란 여러 개의 쓰레드로 동시에
    여러개의 쓰레드를 사용해
    여러 개의 일을 하는 것이다.

프로세스 안에 스레드가 여러 개 존재할 수 있다. 그리고 스레드마다 TCB를 가진다. 각자 컨트롤 블록마다 context를
따로 가지고 있다. 

 

 

멀티쓰레드의 장단점

장점

  • 성능 향상
  • 빠른 응답속도
  • 더 나은 자원 활용 ( CPU Core )
  • 프로세스보다 효율적인 통신 ( 메모리 읽고 쓰기, context switching )

멀티스레드 프로그래밍을 하지 않으면 쿼드 코어인 경우 3개의 코어는 놀고 있다. 
데이터를 주고받을 때도 프로세스끼리 데이터를 주고받는 것보다 스레드끼리 주고받는게 훨씬 빠르다.
context switching을 프로세스간에 하는 것보다 스레드간에 하는 것이 더 오버헤드가 적다.

 

단점

  • 프로그램 복잡도 증가
  • 디버깅의 어려움 ( data race, deadlock )

 

게임 서버에서 멀티 쓰레드를 사용하는 이유

게임 서버에서의 성능은 동접이다. 한 프로그램에서 동접을 최대한 많이 받기 위해 멀티쓰레드를 쓰는 것이다.
게임 서버는 많은 처리를 해야 하는데 해야할 일의 길이가 차이가 있다. 어떤 일은 1초, 어떤 일은 1/10000초만에
작업을 한다. 작업들이 쌓이면 cpu가 처리해야 하는데, 싱글스레드 프로그램일 때 1초짜리 일이 날라오면 1초동안
다른 동작들은 다 멈춰버린다. 1초의 딜레이가 생기는 것이다. 하지만 멀티스레드는 어떤 스레드가 1초동안 어떤 작업을
하느라 묶여 있더라도, 다른 스레드가 다른 작업을 처리하면 된다. 그래서 응답 속도를 높일 수 있다.

 

 

멀티스레드 프로그래밍 주의할 점

  • 쓰레드의 개수가 많다고 좋은 것은 아니다.
  • 코어의 개수에 맞추어라
  • 운영체제 및 하드웨어에 부담

쓰레드의 개수가 多 -> 스택에 차지하는 메모리 多 -> PCB 안에 TCB가 多
-> context 일어날 때 비교할 게 多 -> 운영체제 부담 ↑

 

아무리 개수가 많아도 코어의 개수만큼만 돌아가고 나머지는 waiting하고 있다. -> 코어의 개수에 맞춰서 돌려야 함!!

예를 들어 쿼드코어라면 스레드를 4개 만드는게 제일 성능에 좋다. 8개 만들면 오히려 context switch overhead 때문에
느려진다.

 

 

 

동기적 실행 ( syschronous execution )

  • 서브루틴 간에 명확한 실행 순서 존재
  • A,B,C의 세 가지 서브루틴이 존재하고 A,B,C 순으로 실행되기를 기대한다면 반드시 A->B->C 순서로 실행되어야 
    한다. 즉, B는 A가 실행 완료되기를 기다리고, C는 A와 B가 실행 완료되기를 기다린다.

동기적 실행은 프로그래밍을 처음 배울 때부터 익숙한 개념이다. 일반적으로 코드는 1번째 줄 -> 2번째 줄 ... 식으로
line by line으로 실행되며, 이 순서가 뒤바뀌지 않는다. 

 

비동기적 실행 ( asynchronous execution )

  • 서브루틴 간에 명확한 실행 순서 존재 X
  • A->B->C의 순서로 실행될 수도 있고, B->C->A의 순서로 실행될 수도 있다. 
  • 이런 경우 서브루틴이 완료되었다는 것을 전달하기 위해 callback 패턴을 사용하기도 한다.

 

비동기적 실행은 실행이 뒤바뀔 수 있다. ( 물론 line 단위가 아니라 함수 단위로 비동기화를 한다. )
비동기적 실행은 프로그래밍 시에 구조를 잘 짜 놓지 않으면 예기치 못한 버그가 발생하게 된다. 예를 들어 서로 다른
서브루틴에서 동시에 data write를 시도한다면, 실행 순서에 따라 동작이 달라질 수 있다. 

비동기 기법은 특히 웹 프로그래밍에 매우 유용하다. 웹 앱이 브라우저에서 특정 코드를 실행하느라 브라우저에게
제어권을 돌려주지 않으면 브라우저는 마치 정지된 것처럼 보일 수 있다. 이러한 현상을 blocking이라고 한다.

 

멀티 스레드/코어가 일반화된 현 시점에서는 적절한 비동기화 구조는 상당한 성능 향상을 누릴 수 있다.

 

 

혼동하지 말아야 할 것

  • 동기/비동기적 실행은 멀티 스레딩과 전혀 관련이 없다.
    => 즉 하나의 스레드에서도 비동기적 실행을 할 수 있고, 멀티 스레드에서도 동기적 실행을 할 수 있다.
  • 스레드 or 코어의 개수가 중요한 것이 아니라 서브루틴 간의 실행 순서가 정해져 있는지가 중요하다.
  • 멀티쓰레드 프로그래밍에서 스레드간 크리티컬 섹션 문제 방지를 위한 동기화랑 여기서의 동기/비동기는 다르다!!

 

 

동기와 비동기의 동작 방식

 

동기와 비동기 예시

이렇게 해도 이해가 안 갈 수 있기 때문에 간단한 예시를 들겠다.

 

직원 - 서버, 손님 - 클라이언트

 

1. single thread - 동기 

  •    은행에서 직원 1명이 여러 손님들의 처리가 끝날 때까지 손님은 자기 순서가 올 때까지 줄서서 대기

2. multi thread - 동기

  •    은행에서 직원을 손님 수만큼 증가시킨다. 손님이 없을 때는? -> 비용 낭비

3. single thread - 비동기

  •    은행에서 직원 한명이 일을 보고 손님은 번호표를 뽑고 다른 일 처리

4. multi thread - 비동기

  •    손님의 수 만큼은 아니고 3~4명의 직원이 번호표 처리

 

솔직히 성능으로만 본다면 4번이 최고이나 무조건 4번만 하진 않는다.
왜냐하면 코드 구현이나 성능에 들어가는 비용이 많이 들고 복잡해 지기 때문이다.
그래서 적절하게 상황에 맞게 써야 한다.

 

실제로 웹의 경우는 언제 어디서 누가 몇 명이 어떤 순서로 들어올지 모르는 환경이다. 비동기 통신이기 때문이다.
그렇기 때문에 비용과 성능 측면에서 비동기 방식을 택하게 되는 것이다.

절차지향(Procedural Programming)이란?

물이 위에서 아래로 흐르는 것처럼 순차적인 처리가 중요시 되며 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법이다. 대표적인 절차지향 언어에는 C언어가 있다. 이는 컴퓨터의 작업 처리 방식과 유사하기 때문에 객체지향 언어를 사용하는 것에 비해 더 빨리 처리되어 시간적으로 유리하다. 옛날에는 하드웨어와 소프트웨어의 개발 속도차이가 크지 않았다. 하지만 하드웨어의 빠른 발전을 통해 컴퓨팅 환경은 급속도로 증가했지만 소프트웨어 개발 시간이 따라가지 못하게 되고 이런 상황에 소프트웨어의 개발시간을 단축하되 하드웨어에 기본적인 사양을 잡아먹어도 더 이상 큰 단점이 아니기에 모듈화, 캡슐화해서 개념적으로 접근하는 형태를 갖는 객체지향 프로그래밍이 탄생했다. 객체지향 프로그래밍은 개발하려는 것을 기능별로 묶어 모듈화를 함으로써 같은 기능을 중복으로 연산하지 않거나 모듈을 재활용하기 때문에 유지보수에 유리하다.

 

장점

- 컴퓨터의 처리구조와 유사해 실행속도 빠름

 

단점

- 유지보수가 어려움

- 실행 순서가 정해져 있으므로 코드의 순서가 바뀌면 동일한 결과를 보장하기 어려움

- 디버깅이 어려움

 

 

객체지향(Object Oriented Programming)이란?

객체지향이란 실제 세계를 모델링하여 소프트웨어를 개발하는 방법이다. 객체지향 프로그래밍에서는 데이터와 절차를 하나의 덩어리로 묶어서 생각한다. 이는 마치 컴퓨터 부품을 하나씩 사다가 컴퓨터를 조립하는 것과 같은 방법이다.

 

객체지향의 4대 특성

1. 추상화

  • 객체 지향적 관점에서 클래스를 정의하는 것. 예를 들어 사자, 고양이, 강아지가 있을 때 우리는 이것을
    각각 객체라고 하며, 이 객체들의 공통점인 동물이라고 표현할 수 있는데 이때 동물로 묶는 행위를
    추상화라고 한다.

2. 캡슐화

  • 특정 객체가 독립적으로 역할을 수행하기 위해 필요한 데이터와 기능을 하나로 묶는 것.
    ☞ 쉽게 말해 모듈화를 의미한다. 이러한 캡슐화를 통해 정보를 객체 안에 포함시키고, 그 정보에 대한 직접
        접근은 허용하지 않는 대신, 필요에 따라 확인할 수 있는 인터페이스를 외부에 공개함으로써 정보 은닉
        효과도 자연스럽게 따라온다.

3. 상속

  • 상위 개념의 특징을 하위 개념이 물려받는 것.

    상속에서 주의할 점
    ☞ 계층도, 조직도 관점(가족 관계도와 같은)에서 이해하면 안된다.
    분류도 관점에서 이해해야 한다.
    하위 클래스는 상위 클래스의 역할을 대신할 수 있으면서 고유의 역할도 수행할 수 있어야 한다.
          아버지와 아들을 예로 들면 아들은 아버지의 역할을 할 수 있으면서 아들 고유의 역할도 수행할 수 있어야
          하지만 그렇지 못하다. 이런 부분에서 상속을 사용할 때 주의해야 한다는 것이다.
    상속은 코드의 재사용성을 높이고 확장성을 높여준다.

4. 다형성

  • 다형성이란 하나의 이름으로 많은 상황에 대처하는 기법이다. 오버로딩, 오버라이딩이 그 방법이다.
    다형성을 이요하면 코드가 더 간단해지는 효과가 있다.

 

장점

- 코드의 재활용성이 높음

- 코딩이 절차지향보다 간편함

- 디버깅이 쉬움

 

단점

- 처리속도가 절차지향보다 느림

- 설계에 많은 시간소요가 들어감

 

이론적으로만 본다면 객체지향이 절차지향 언어에 비해 장점이 많다. 하지만 프로그래밍을 할 떼 항상 객체지향 언어만 사용하는 것은 아니다. 객체지향 언어는 어떤 모듈에 있는 하나의 기능만 필요하더라도 모듈 전체를 가져와야 하기 때문에 절차지향 프로그래밍보다 프로그램 사이즈가 더 커질 수도 있다. 또한 데이터에 대한 접근도 상대적으로 절차지향식보다 느려질 가능성이 많다. 

 

객체지향과 절차지향의 차이점

객체지향의 반대는 절차지향이 아니고 절차지향의 반대는 객체지향이 아니다. 

절차지향  ->  순차적으로 실행에 초점이 되어 있음 , 데이터 중심

객체지향  ->  객체간의 관계/조직에 초점을 두고 있음, 기능 중심

 

프로세스와 스레드의 차이

프로세스(Process)

  • 실행 중인 프로그램. 즉 디스크로부터 메모리에 적재되어 CPU의 할당을 받을 수 있는 것을 말한다.
  • 운영체제로부터 주소 공간, 파일, 메모리 등을 할당받으며 이것들을 총칭하여 프로세스라고 한다.

프로세스 제어 블록 (Process Control Blcok, PCB)

  PCB는 특정 프로세스에 대한 중요한 정보를 저장하고 있는 운영체제의 자료구조이다.
운영체제는 프로세스를 관리하  기 위해 프로세스의 생성과 동시에 고유한 PCB를 생성한다.
프로세스는 CPU를 할당받아 작업을 처리하다가도 프로세스 전환이 발생하면 진행하던 작업을 저장하고
CPU를 반환해야 하는데, 이때 작업의 진행 상황을 모두 PCB에 저장하게 된다.
그리고 다시 CPU를 할당받게 되면
PCB에 저장되어 있던 내용을 불러와 이전에 종료되었던 시점부터 다시 작업을 수행한다.

 

PCB에 저장되는 정보

  • 프로세스 식별자 ( Process ID, PID )
  • 프로세스 상태 ( new, ready, running, waiting, terminated 등의 상태를 저장 )
  • 프로그램 카운터 ( 프로세스가 다음에 실행할 명령어의 주소 )
  • CPU 레지스터
  • CPU 스케줄링 정보 ( 프로세스의 우선순위, 스케줄 큐에 대한 포인터 등 )
  • 메모리 관리 정보 ( 페이지 테이블 or 세그먼트 테이블 등과 같은 정보를 포함 )
  • 입출력 상태 정보
  • 어카운팅 정보 ( 사용된 cpu 시간, 시간제한 계정본호 등 )

스레드 ( Thread )

  • 프로세스의 실행 단위
  • 한 프로세스 내에서 동작되는 여러 실행 흐름
  • 같은 프로세스에 속한 스레드들끼리는 코드 영역, 데이터 영역, 힙 영역, 그리고 열린 파일이나 신호와 같은
    운영체제 자원들을 공유한다.
  • 하나의 프로세스를 다수의 실행 단위로 구분하여 자원을 공유하고 자원의 생성과 관리의 중복성을 최소화하여
    수행 능력을 향상시키는 것을 멀티스레딩이라고 한다. 이 경우 각각의 스레드는 독립적인 작업을 수행해야 하기
    때문에 각자의 스택과 pc 레지스터 값을 가지고 있다.

쓰레드 자원 공유 형태

스택을 스레드마다 독립적으로 할당하는 이유

  스택 메모리 공간이 독립적이라는 것은 독립적인 함수 호출이 가능하다는 것이고 이는 독립적인 실행 흐름이 추가되는 것이다. 따라서 스레드의 정의에 따라 독립적인 실행 흐름을 추가하기 위한 최소 조건으로 독립된 스택을 할당한다.

 

PC register를 스레드마다 독립적으로 할당하는 이유

  PC값은 스레드가 명령어의 어디까지 수행하였는지를 나타내게 된다. 스레드는 CPU를 할당받았다가 스케줄러에 의해 다시 선점당한다. 그렇기 때문에 명령어가 연속적으로 수행되지 못하고 어느 부분까지 수행했는지 기억할 필요가 있다. 따라서 PC 레지스터를 독립적으로 할당해야 한다.

 

멀티 스레드

멀티 스레딩의 장점

  프로세스를 이용하여 동시에 처리하던 일을 스레드로 구현할 경우 메모리 공간과 시스템 자원 소모가 줄어들게 된다. 스레드간의 통신이 필요한 경우에도 별도의 자원을 이용하는 것이 아니라 전역 변수의 공간 or 동적할당된 공간인 Heap 영역을 이용하여 데이터를 주고받을 수 있다. 그렇기 때문에 프로세스 간 통신 방법에 비해 스레드 간의 통신 방법이 훨씬 간단하다. 심지어 스레드의 context switching은 프로세스의 context switching과 달리 캐시 메모리를 비울 필요가 없기 때문에 더 빠르다. 따라서 시스템의 자원 소모가 줄어들며 자연스럽게 프로그램의 응답 시간이 단축된다. 이러한 장점 때문에 여러 프로세스로 할 수 있는 작업들을 하나의 프로세스에서 스레드로 나눠 수행하는 것이다.

 

멀티 스레딩의 문제점

  멀티 프로세스 기반으로 프로그래밍 할 때는 프로세스 간 공유하는 자원이 없기 때문에 동일한 자원에 동시에 접근하는 일이 없었지만 멀트 스레딩을 기반으로 프로그래밍 할 때는 이 부분을 신경써줘야 한다. 서로 다른 스레드가 데이터와 힙 영역을 공유하기 때문에 어떤 스레드가 다른 스레드에서 사용중인 변수나 자료구조에 접근하여 엉뚱한 값을 읽어오거나 수정할 수가 있다.

 

  그렇기 때문에 멀티스레딩 환경에서는 동기화 작업이 필요하다. 동기화를 통해 작업 처리 순서를 컨트롤하고 공유 자원에 대한 접근을 컨트롤 하는 것이다. 하지만 이로 인해 병목현상이 발생하여 성능이 저하될 가능성이 높다. 그러므로 과도한 락으로 인한 병목현상을 줄여야 한다. 그리고 디버깅이 어렵다.

 

멀티 스레드 vs 멀티 프로세스

  멀티 스레드는 멀티 프로세스보다 적은 메모리 공간을 차지하고 문맥 전환이 빠르다는 장점이 있지만, 오류로 인해 하나의 스레드가 종료되면 전체 스레드가 종료될 수 있다는 점과 동기화 문제를 안고 있다. 반면 멀티 프로세스 방식은 하나의 프로세스가 죽더라도 다른 프로세스에는 영향을 끼치지 않고 정상적으로 수행된다는 장점이 있지만, 멀티 스레드보다 많은 메모리 공간과 CPU 시간을 차지한다는 단점이 존재한다. 이 두 가지는 동시에 여러 작업을 수행한다는 점에서 같지만 적용해야 하는 시스템에 따라 적합/부적합으로 구분된다. 따라서 대상 시스템의 특징에 따라 적합한 동작 방식을 선택하고 적용해야 한다.

 

프로세스 동기화

Critical Section(임계영역)

  • 멀티 스레딩에 문제점에서 나오듯, 동일한 자원을 동시에 접근하는 작업을 실행하는 코드 영역을 Critical Section
    이라 칭한다.

Critical Section Problem(임계영역 문제)

프로세스들이 Critical Section을 함께 사용할 수 있는 프로토콜을 설계해야 한다.

 

Requirements( 해결을 위한 기본 조건 )

  • Mutual Exclusion ( 상호 배제 )
    - 프로세스 p1이 Critical Section에서 실행 중이라면 다른 프로세스들은 그들이 가진 Critical Section에서 실행될 수 
      없다.
  • Progress(진행)
    - Critical Section에서 실행중인 프로세스가 없고, 별도의 동작이 없는 프로세스들만 Critical Section 진입 후부로서
       참여될 수 있다.
  • Bounded Waiting ( 한정된 대기 )
    - p1이 Critical Section 에 진입 신청 후 받아들여질 때까지, 다른 프로세스들이 Critical Section에 진입하는 횟수는 
      제한이 있어야 한다.

Critical Section Problem 해결책

Lock

   - 하드웨어 기반 해결책으로써, 동시에 공유 자원에 접근하는 것을 막기 위해 Critical Section 에 진입하는 프로세스는
      Lock 을 획득하고 Critical Section 을 빠져나올 때, Lock 을 방출함으로써 동시에 접근이 되지 않도록 한다.

   - lock은 다중처리기 환경에서는 시간적인 효율성 측면에서 적용할 수 없다.

 

Semaphores(세마포)

  • 소프트웨어상에서 Critical Section 문제를 해결하기 위한 동기화 도구

Semaphores 종류

  • 카운팅 세마포
    가용한 개수를 가진 자원 에 대한 접근 제어용으로 사용되며, 세마포는 그 가용한 자원의 개수 로 초기화 된다.
    자원을 사용하면 세마포가 감소, 방출하면 세마포가 증가 한다.

  • 이진 세마포
    MUTEX 라고도 부르며, 상호배제의 (Mutual Exclusion)의 머릿글자를 따서 만들어졌다.
    이름 그대로 0 과 1 사이의 값만 가능하며, 다중 프로세스들 사이의 Critical Section 문제를 해결하기 위해 사용한다.

Semaphores 단점

  • Busy Waiting(바쁜 대기)
    - Semaphore 초기 버전에서 Critical Section 에 진입해야하는 프로세스는 진입 코드를
      계속 반복 실행해야 하며, CPU 시간을 낭비했었다. 이를 Busy Waiting이라고 부르며 특수한 상황이
      아니면 비효율적이다.

    - 일반적으로는 Semaphore에서 Critical Section에 진입을 시도했지만 실패한 프로세스에 대해 Block시킨 뒤,
      Critical Section에 자리가 날 때 다시 깨우는 방식을 사용한다. 이 경우 Busy waiting으로 인한
      시간낭비 문제가 해결된다.

Deadlock(교착상태)

  • 세마포가 Ready Queue 를 가지고 있고, 둘 이상의 프로세스가 Critical Section 진입을
    무한정 기다리고 있고, Critical Section 에서 실행되는 프로세스는 진입 대기 중인 프로세스가 실행되어야만
    빠져나올 수 있는 상황을 지칭한다.

 

스케줄러

프로세스를 스케줄링하기 위한 Queue에는 세 가지 종류가 존재한다.

  • Job Queue : 현재 시스템 내에 있는 모든 프로세스의 집합
  • Ready Queue : 현재 메모리 내에 있으면서 CPU를 잡아서 실행되기를 기다리는 프로세스의 집합
  • Device Queue : Device I/O 작업을 대기하고 있는 프로세스의 집합

각각의  Queue에 프로세스들을 넣고 빼주는 스케줄러에도 크게 세 가지 종류가 존재한다.

  • 장기 스케줄러 ( Long-term scheduler or job scheduler )
    메모리는 한정되어 있는데 많은 프로세스들이 한꺼번에 메모리에 올라올 경우, 대용량 메모리(일반적으로 디스크)에
    임시로 저장된다. 이 pool에 저장되어 있는 프로세스 중 어 떤 프로세스에 메모리를 할당하여 ready queue로 보낼지 결정하는 역할을 한다.
      -  메모리와 디스크 사이의 스케줄링을 담당
      -  프로세스에 memory를 할당 ( admit )
      -  degree of Multiprogramming 제어 ( 실행중인 프로세스의 수 제어 )
      -  프로세스의 상태 ( new -> ready )

  • 단기 스케줄러 ( Short-term scheduler or CPU scheduler )
      -  CPU와 메모리 사이의 스케줄링을 담당
      -  Ready Queue 에 존재하는 프로세스 중 어떤 프로세스를 running 시킬지 결정
      -  프로세스에 CPU를 할당 ( scheduler dispatch )
      -  프로세스의 상태 ( read -> running -> waiting -> ready )

  • 중기 스케줄러 ( Medium-term scheduler or Swapper )
      -  여유 공간 마련을 위해 프로세스를 통째로 메모리에서 디스크로 쫓아냄 (swapping)
      -  프로세스에게서 memory를 deallocate
      -  degree of Multiprogramming 제어 ( 동시에 처리 가능한 수 작업이나 프로그램의 수 )
      -  현 시스템에서 메모리에 너무 많은 프로그램이 동시에 올라가는 것을 조절하는 스케줄러
      -  프로세스의 상태 ( ready -> suspended )

CPU 스케줄러

스케줄링 대상은 Ready Queue에 있는 프로세스들이다.

 

1. FIFO(First In First Out)

  • 먼저 온 고객을 먼저 서비스 해주는 방식, 즉 먼저 온 순서대로 처리
  • 비선점형(Non-Preemptive) 스케줄링
    일단 CPU를 잡으면 CPU burst가 완료될 때까지 CPU를 반환하지 않는다. 할당되었던 CPU가 반환될 때만
    스케줄링이 이루어진다.

문제점

  • convoy effect
    - 소요시간이 긴 프로세스가 먼저 도달하여 효율성을 낮추는 현상 발생

 

2. SJF(Shortest - Job - First)

  • 다른 프로세스가 먼저 도착했어도 CPU burst time이 짧은 프로세스에게 선 할당
  • 비선점형(Non-Preemptive) 스케줄링

문제점

  • starvation
    -  효율성을 추구하는게 가장 중요하지만 특정 프로세스가 지나치게 차별받으면 안된다. 이 스케줄링 기법은
       극단적으로 CPU 사용이 짧은 job을 선호한다. 그래서 사용 시간이 긴 프로세스는 거의 영원히 CPU를
       할당받을 수 없다.

 

3. SRTF(Shortest Remaining Time First)

  • 현재 수행중인 프로세스의 남은 burst time보다 더 짧은 CPU burst time을 가지는 새로운 프로세스가 도착하면
    CPU를 뺏긴다.
  • 선점형(Preemptive) 스케줄링

문제점

  • starvation
    -  새로운 프로세스가 도달할 때마다 스케줄링을 다시하기 때문에 CPU burst time(CPU 사용시간)을 측정할 수가 없다.

 

4. Priority Scheduling

  • 우선순위가 가장 높은 프로세스에게 CPU를 할당하는 스케줄링이다.
    ※우선순위 : 정수로 표현하고 작은 숫자가 우선순위가 높다.
  • 선점형(Preemptive) 스케줄링
    - 더 높은 우선순위의 프로세스가 도착하면 Ready Queue의 Head에 넣는다.

문제점

  • starvation
  • 무기한 봉쇄(Indefinite blocking)
    - 실행 준비는 되어있으나 우선순위가 낮아 CPU를 사용 못하는 프로세스가 무기한 대기하는 상태

해결 방법

  • aging
    - 아무리 우선순위가 낮은 프로세스라도 오래 기다리면 우선순위를 높여준다.

 

5. Round Robin

  • 현대적인 CPU 스케줄링
  • 각 프로세스는 동일한 크기의 할당 시간(time quantum)을 갖게 된다.
  • 할당 시간이 지나면 프로세스는 선점당하고 ready queue의 제일 뒤에 가서 다시 줄을 선다.
  • CPU 사용시간이 랜덤한 프로세스들이 섞여있을 경우 효율적
  • RR이 가능한 이유는 프로세스의 context를 save할 수 있기 때문이다.

장점

  • Response time이 빨라진다.
  • 프로세스가 기다리는 시간이 CPU를 사용할 만큼 증가한다. => 공정한 스케줄링이다. 

주의할 점

  • 설정한 time quantu이 너무 커지면 FIFO와 같아진다. 또 너무 작아지면 스케줄링 알고리즘의 목적에는
    이상적이지만 잦은 context switch로 overload가 발생한다. 그렇기 때문에 적당한 time quantum을 설정
    하는 것이 중요하다.

 

     

좋은 코드란?

  • 읽기 쉬운 코드
  • 중복이 없는 코드
  • 테스트가 용이한 코드

Object Oriented Programming ( 객체지향 프로그래밍 )

객체 지향 프로그래밍 이전의 프로그래밍 패러다임을 살펴보면, 중심이 컴퓨터에 있었다. 컴퓨터가 사고하는대로 프로그래밍을 하는 것이다. 하지만 객체지향 프로그래밍은 인간 중심적 프로그래밍 패러다임이라고 할 수 있다. 즉, 현실 세계를 프로그래밍으로 옮겨와 프로그래밍하는 것을 말한다. 현실 세계의 사물들을 객체라고 보고 그 객체로부터 개발하고자 하는 애플리케이션에 필요한 특징들을 뽑아와 프로그래밍 하는 것이다. 이것을 추상화라고 한다.

 

OOP로 코드를 작성하면 이미 작성한 코드에 대한 재사용성이 높다. 자주 사용되는 로직을 라이브러리로 만들어두면 계속해서 사용할 수 있으며 그 신뢰성을 확보할 수 있다. 또한 라이브러리를 각종 예외상황에 맞게 잘 만들어두면 개발자가 사소한 실수를 하더라도 그 에러를 컴파일 단계에서 잡아낼 수 있으므로 버그 발생이 줄어든다. 또한 내부적으로 어떻게 동작하는지 몰라도 개발자는 라이브러리가 제공하는 기능들을 사용할 수 있기 때문에 생산성이 높아지게 된다. 객체 단위로 코드가 나눠져 작성되기 때문에 디버깅이 쉽고 유지보수에 용이하다. 또한 데이터 모델링을 할 때 객체와 매핑하는 것이 수월하기 때문에 요구사항을 보다 명확하게 파악하여 프로그래밍 할 수 있다.

 

객체 간의 정보 교환이 모두 메시지 교환을 통해 일어나므로 실행 시스템에 많은 오버헤드가 발생하게 된다. 하지만 이것은 하드웨어의 발전으로 많은 부분 보안되었다. 객체 지향 프로그래밍의 치명적인 단점은 함수형 프로그래밍 패러다임의 등장 배경을 통해서 알 수 있다. 바로 객체가 상태를 갖는다는 것이다. 변수가 존재하고 이 변수를 통해 객체가 예측할 수 없는 상태를 갖게 되어 애플리케이션 내부에서 버그를 발생시킨다는 것이다. 이러한 이유로 함수형 패러다임이 주목받고 있다.

 

객체 지향적 설계 원칙 ( SOLID ) ★

  1.  SRP(Single Responsibility Principle)
    - 단일 책임 원칙
    - 클래스는 단 하나의 책임을 가져야 하며 클래스를 변경하는 이유는 단 하나의 이유이어야 한다.
  2. OCP(Open-Closed Principle)
    - 개방-폐쇄 원칙
    - 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
  3. LSP(Liskov Substitution Principle)
    - 리스코프 치환 원칙
    - 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
  4. ISP(Interface Segregation Principle)
    - 인터페이스 분리 원칙
    - 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.
  5. DIP(Dependency Inversion Principle)
    - 의존 역전 원칙
    - 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다.

 

RESTful API

월드 와이드 웹(World Wide Web a.k.a WWW)과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식으로 자원을 정의하고 자원에 대한 주소를 지정하는 방법 전반에 대한 패턴

REST는 하나의 아키텍처로 볼 수 있다. 좀 더 정확한 표현으로 말하자면, REST는 API 설계의 중심에 자원이 있고 
HTTP Method를 통해 자원을 처리하도록 설계하는 것이다.

 

REST의 특징

  • Uniform Interface
  • Stateless ( 무상태성 )
  • Cacheable ( 캐시 가능 )
  • Client-Server 구조
  • Hierarchical system ( 계층형 구조 )
  • Self-descriptiveness ( 자체 표현 구조 )

RESTful하게 API를 디자인 한다는 것은 무엇을 의미하는가

  1.  리소스와 행위를 명시적이고 직관적으로 분리한다.
    - 리소스  =>  URI로 표현되는데 리소스가 가리키는 것은 명사로 표현되어야 한다.
    - 행위     =>  HTTP Method로 표현하고, GET(조회), POST(생성), PUT(기존 entity 전체 수정),
                            PATCH(기존 entity 일부 수정), DELETE(삭제) 를 분명한 목적으로 사용한다.

  2.  Message는 Header와 Body를 명확하게 분리해서 사용한다.
    - Entity에 대한 내용은 body에 담는다.
    - 애플리케이션 서버가 행동할 판단의 근거가 되는 컨트롤 정보인 API 버전 정보, 응답받고자 하는 MIME 타입 등은
       header에 담는다.
    - header와 body는 http header와 http body로 나눌 수도 있고, http body에 들어가는 json 구조로 분리할 수도 있다.
     
  3.  API 버전을 관리한다.
    - 환경은 항상 변하기 때문에 API의 signature가 변경될 수도 있음에 유의하자.
    - 특정 API를 변경할 때는 반드시 하위호환성을 보장해야 한다.

  4.  서버와 클라이언트가 같은 방식을 사용해서 요청하도록 한다.
    - 브라우저는 form-data 형식의 submit으로 보내고 서버에서는 json 형태로 보내는 식의 분리보다는
       json으로 보내든, 둘 다 form-data 형식으로 보내든 하나로 통일한다.
    - 다른 말로 표현하자면 URI가 플랫폼 중립적이어야 한다.

 

장점

  • Open API를 제공하기 쉽다.
  • 멀티플랫폼 지원 및 연동이 용이하다.
  • 원하는 타입으로 데이터를 주고 받을 수 있다.
  • 기존 웹 인프라(HTTP)를 그대로 사용할 수 있다.

단점

  • 사용할 수 있는 메소드가 4가지 밖에 없다.
  • 분산환경에서는 부적합하다.
  • HTTP 통신 모델에 대해서만 지원한다.

 

자세한 REST API에 대한 정보 : https://meetup.toast.com/posts/92

 

TDD

TDD란?

 Test-Driven Development(TDD)는 매우 짧은 개발 사이클의 반복에 의존하는 소프트웨어 개발 프로세스이다. 우선 개발자는 요구되는 새로운 기능에 대한 자동화된 테스트케이스를 작성하고 해당 테스트를 통과하는 가장 간단한 코드를 작성한다. 일단 테스트 통과하는 코드를 작성하고 상황에 맞게 리팩토링하는 과정을 거치는 것이다. 말 그대로 테스트가 코드 작성을 주도하는 개발방식인 것이다.

 

Add a test

 테스트 주도형 개발에선, 새로운 기능을 추가하기 전 테스트를 먼저 작성한다. 테스트를 작성하기 위해서, 개발자는 해당 기능의 요구사항과 명세를 분명히 이해하고 있어야 한다. 이는 사용자 케이스와 사용자 스토리 등으로 이해할 수 있으며, 이는 개발자가 코드를 작성하기 전에 요구사항에 집중할 수 있도록 도와준다. 이는 정말 중요한 부분이자 테스트 주도 개발이 주는 이점이라고 볼 수 있다.

 

Run all tests and see if new one fails

 어떤 새로운 기능을 추가하면 잘 작동하던 기능이 제대로 작동하지 않는 경우가 발생할 수 있다. 더 위험한 경우는 개발자가 이를 미처 인지하지 못하는 경우이다. 이러한 경우를 방지하기 위해 테스트 코드를 작성하는 것이다. 새로운 기능을 추가할 때 테스트 코드를 작성함으로써, 새로운 기능이 제대로 작동함과 동시에 기존의 기능들이 잘 작동하는지 테스트를 통해 확인할 수 있는 것이다.

 

Refactor code

 '좋은 코드'를 작성하기란 쉽지 않다. 코드를 작성할 때 고려해야 할 요소가 한 두 가지가 아니기 때문이다. 가독성이 좋게 coding convention을 맞춰야 하며, 네이밍 규칙을 적용하여 메소드명, 변수명, 클래스명에 일관성을 줘야 하며, 앞으로의 확장성 또한 고려해야 한다. 이와 동시에 비즈니스 로직에 대한 고려도 반드시 필요하며, 예외처리 부분 역시 빠뜨릴 수 없다. 물론 코드량이 적을 때는 이런 저런 것들을 모두 신경쓰면서 코드를 작성할 수 있지만 끊임없이 발견되는 버그들을 디버깅하는 과정에서 코드가 더럽혀지기 마련이다.

 

 이러한 이유로 코드량이 방대해지면 리팩토링을 하게 된다. 이 때 테스트 주도 개발을 통해 개발을 해 왔다면, 테스트 코드가 그 중심을 잡아줄 수 있다. 뚱뚱해진 함수를 여러 함수로 나누는 과정에서 해당 기능이 오작동을 일으킬 수 있지만 간단히 테스트를 돌려봄으로써 이에 대한 안심을 하고 계속해서 리팩토링을 진행할 수 있다. 결과적으로 리팩토링 속도도 빨라지고 코드의 퀄리티도 그만큼 향상하게 되는 것이다. 코드 퀄리티 부분을 조금 상세히 들어가보면, 보다 객체지향적이고 확장 기능이 용이한 코드, 재설계의 시간을 단축시킬 수 있는 코드, 디버깅 시간이 단축되는 코드가 TDD와 함께 탄생하는 것이다.

 

 어차피 코드를 작성하고나서 제대로 작동하는지 판단해야하는 시점이 온다. 물론 중간 중간 수동으로 확인도 할 것이다. 또 테스트에 대한 문서도 만들어야 한다. 그 부분을 자동으로 해주면서, 코드 작성에 도움을 주는 것이 TDD인 것이다. 끊임없이 TDD 찬양에 대한 말만 했다. TDD를 처음 들어보는 사람은 이 좋은 것을 왜 안하는가에 대한 의문이 들 수도 있다.

 

의문점

Q. 코드 생산성에 문제가 있지는 않나?

  두 배는 아니더라도 분명 코드량이 늘어난다. 비즈니스 로직, 각종 코드 디자인에도 시간이 많이 소요되는데, 거기에다가 테스트 코드까지 작성하기란 여간 벅찬 일이 아닐 것이다. 코드 퀄리티보다는 빠른 생산성이 요구되는 시점에서 TDD 는 큰 걸림돌이 될 수 있다.

 

Q. 테스트 코드를 작성하기가 쉬운가?

  이 또한 TDD 라는 개발 방식을 적용하기에 큰 걸림돌이 된다. 진입 장벽이 존재한다는 것이다. 어떠한 부분을 테스트해야할 지, 어떻게 테스트해야할 지, 여러 테스트 프레임워크 중 어떤 것이 우리의 서비스와 맞는지 둥 여러 부분들에 대한 학습이 필요하고 익숙해지는데에도 시간이 걸린다. 팀에서 한 명만 익숙해진다고 해결될 일이 아니다. 개발은 팀 단위로 수행되기 때문에 팀원 전체의 동의가 필요하고 팀원 전체가 익숙해져야 비로소 테스트 코드가 빛을 발하게 되는 것이다.

 

Q. 모든 상황에 대해서 테스트 코드를 작성할 수 있는가? 작성해야 하는가?

  세상에는 다양한 사용자가 존재하며, 생각지도 못한 예외 케이스가 존재할 수 있다. 만약 테스트를 반드시 해봐야 하는 부분에 있어서 테스트 코드를 작성하는데 어려움이 발생한다면? 이러한 상황에서 주객이 전도하는 상황이 발생할 수 있다. 분명 실제 코드가 더 중심이 되어야 하는데 테스트를 위해서 코드의 구조를 바꿔야 하나하는 고민이 생긴다. 또한 발생할 수 있는 상황에 대한 테스트 코드를 작성하기 위해 배보다 배꼽이 더 커지는 경우가 허다하다. 실제 구현 코드보다 방대해진 코드를 관리하는 것도 쉽지만은 않은 일이 된 것이다.

모든 코드에 대해서 테스트 코드를 작성할 수 없으며 작성할 필요도 없다. 또한 테스트 코드를 작성한다고 해서 버그가 발생하지 않는 것도 아니다. 애초에 TDD 는 100% coverage 와 100% 무결성을 주장하지 않았다.

 

MVC 아키텍쳐

MVC의 각 컴포넌트의 역할

 

Model( 모델 )
- 컨트롤러가 호출할 때 요청에 맞는 역할을 수행한다.
- 비즈니스 로직( = 업무에 필요한 데이터 처리 수행 )을 구현하는 영역으로 응용프로그램에서 데이터를 처리하는 부분이다.
- DB에 연결하고 데이터를 추출하거나 저장, 삭제, 업데이트, 변환 등의 작업을 수행한다.
- 상태의 변화가 있을 때 컨트롤러와 뷰에 통보해 후속 조치 명령을 받을 수 있게 한다.

 

View( 뷰 )

- 컨트롤러로부터 받은 모델의 결과값을 가지고 사용자에게 출력할 화면을 만드는 일을 한다.
- 만들어진 화면을 웹브라우저에 전송하여 웹브라우저가 출력하게 한다.
- 사용자와의 상호작용을 위한 인터페이스를 표시하는 영역

 

Controller ( 컨트롤러 )

- 일종의 조정자라고 할 수 있다. 클라이언트의 요청을 받았을 때, 그 요청에 대해 실제 업무를 수행하는 모델 컴포넌트를 호출한다.
- 클라이언트가 보낸 데이터가 있다면, 모델에 전달하기 쉽게 데이터를 가공한다. 모델이 업무를 마치면 그 결과를 뷰에 전달한다.

 

 

MVC 구동 원리

C/S(Client - Server) 구조로 요청을 하면 그에 맞는 응답을 하는 구조를 기본으로 하고 있다.

1. 웹 브라우저가 웹 서버에 웹 애플리케이션 실행을 요청 ( MVC 구조가 WAS라고 보면 된다. )
2. 웹 서버는 들어온 요청을 처리할 수 있는 서블릿을 찾아서 요청을 전달한다. ( Matching )

3. 서블릿은 모델 자바 객체의 메서드를 호출한다.

4. 데이터를 가공하여 값 객체를 생성하거나, JDBC를 사용하여 데이터베이스와의 인터랙션을 통해 값 객체를 생성한다.

5. 업무 수행을 마친 결과값을 컨트롤러에게 반환한다.

6. 컨트롤러는 모델로부터 받은 결과값을 View에게 전달한다.

7. JSP는 전달받은 값을 참조하여 출력할 결과 화면을 만들고 컨트롤러에게 전달한다.

8. 뷰로부터 받은 화면을 웹 서버에게 전달한다.

9. 웹 브라우저는 웹 서버로부터 요청한 결과값을 응답받으면 그 값을 화면에 출력한다.

 

 

+ Recent posts