사전 실행
개념

실행

gabury1고친 사람 github-actions[bot]

실행은 무엇을 할지 알아낸 다음 그 일을 해내는 단계입니다. 읽고 뜻을 파악하는 앞 단계가 끝나야 실행이 시작됩니다. 일상에서는 프로그램을 돌리는 일 전체를 실행이라고 부릅니다. 컴퓨터 안의 처리 과정에서는 그중 한 단계를 가리킵니다.

쉽고 빠른 이해

실행은 지시를 이해한 뒤에 실제로 수행하는 단계입니다. 컴퓨터의 계산 장치는 명령어를 읽고 무슨 명령인지 알아냅니다. 덧셈이면 그때 덧셈을 합니다. 그 덧셈이 실행입니다.

이해하는 단계와 수행하는 단계를 나누는 데는 까닭이 있습니다. 잘못된 지시를 수행하기 전에 걸러낼 수 있습니다. 데이터를 반쯤 바꾸다 멈추는 일이 줄어듭니다.

어떻게 도나:

  1. 지시를 읽어 옵니다
  2. 무슨 뜻인지 알아내고 문제가 없는지 봅니다
  3. 알아낸 대로 수행합니다. 이 단계가 실행입니다

대가도 있습니다. 앞 단계가 알아낸 내용을 실행 단계로 건네줘야 하니 할 일이 하나 늘어납니다. 그리고 수행하는 도중에야 드러나는 문제는 앞 단계가 막아 주지 못합니다.

상세

식당 주방을 떠올려 봅니다. 주문서를 받으면 먼저 무슨 요리인지 읽습니다. 재료가 있는지도 확인합니다. 그다음에야 불을 켜고 요리합니다. 요리하는 그 시간이 실행에 해당합니다.

실행은 해석이 끝난 지시를 수행하는 단계입니다. 해석은 지시가 무슨 뜻인지 알아내는 일입니다. 아래에 나올 CPU(Central Processing Unit, 중앙처리장치)의 해독, GraphQL 서버의 파싱·검증이 모두 이 해석에 속합니다. 실행은 알아낸 뜻대로 값을 계산하고, 데이터를 읽고 쓰고, 다음 할 일을 정합니다.

맥락마다 가리키는 범위

「실행」은 여러 분야에서 씁니다. 가리키는 범위가 맥락마다 다릅니다. 이 문서는 아래 표의 둘째와 셋째 줄, 곧 처리 과정 안의 한 단계를 중심으로 봅니다.

표의 셋째 칸은 실행 앞에 오는 단계입니다. 인출·해독은 CPU 절에서, 파싱·검증은 GraphQL 절에서 풀어 씁니다. 나머지 둘은 괄호 안에 짧게 적었습니다.

맥락 실행이 가리키는 것 실행 앞에 오는 단계
프로그램 프로그램을 띄워 끝날 때까지 돌리는 일 전체 컴파일(소스 코드를 기계가 읽는 꼴로 바꾸기) · 설치
중앙처리장치 명령어 하나를 처리하는 단계 중 계산하는 단계 인출 · 해독
요청 처리 받은 질의를 해석한 대로 수행하는 단계 파싱 · 검증
테스트 테스트할 코드를 불러 보는 단계 준비(입력값과 필요한 객체를 마련하기)

네 줄에 공통된 모양이 있습니다. 앞 단계는 실행이 곧바로 손댈 수 있게 갖춰 둡니다. 실행은 갖춰진 것을 받아 수행합니다. 범위가 크든 작든 이 나눔은 같습니다.

앞 단계와 나누는 까닭

GraphQL 서버가 요청 하나를 받았다고 해 봅니다. 요청에는 데이터를 바꾸라는 지시가 여러 개 적혀 있을 수 있습니다. 읽자마자 앞에서부터 수행하면 곤란합니다. 뒤쪽 지시에 틀린 곳이 있어도 앞쪽은 이미 데이터를 바꾼 뒤라 되돌리기 어렵습니다.

그래서 먼저 요청을 끝까지 읽습니다. 문제가 없는지 본 다음 실행에 들어갑니다.

해석한 결과를 다시 쓸 수 있다는 점도 있습니다. 같은 요청이 여러 번 오면 파싱과 검증은 한 번만 하고 실행만 되풀이할 수 있습니다. 해석에 드는 시간을 아끼는 셈입니다.

나눠도 모든 오류를 미리 잡지는 못합니다. 0 으로 나누기나 없는 데이터 읽기는 값을 넣어 돌려 봐야 드러납니다. 실행 중에 나는 이런 오류를 런타임 오류라고 합니다.

CPU 의 실행 단계

CPU는 명령어 하나를 몇 단계로 나눠 처리합니다. 먼저 메모리에서 명령어를 읽어 오는 인출이 있습니다. 읽어 온 비트가 무슨 명령인지 알아내는 해독이 뒤따릅니다. 그다음이 실행입니다.

실행 단계에서는 해독이 알려 준 계산을 합니다. 덧셈·뺄셈 같은 산술과 AND·OR 같은 논리 계산입니다. 이 계산은 ALU(Arithmetic Logic Unit, 산술 논리 장치)라는 회로가 맡습니다. 두 값을 받아 명령이 고른 계산을 한 뒤 결과 하나를 냅니다.

계산만 하는 것이 아닙니다. 분기 명령이면 조건이 맞는지 따집니다. 맞으면 다음에 읽을 주소를 바꿉니다. 그 주소는 프로그램 카운터에 담겨 있습니다. 메모리를 읽고 쓰는 명령이면 어느 주소에 손댈지를 계산합니다.

CPU 안에는 레지스터라는 작은 저장 공간이 있습니다. 계산하는 동안 쓰는 값을 담아 두는 곳입니다. 실행이 낸 결과를 레지스터나 메모리에 적는 일은 실행 뒤의 결과 쓰기 단계가 맡습니다.

단계를 이렇게 잘게 나누면 명령어 여러 개를 겹쳐 처리할 수 있습니다. 첫 명령어가 해독에 들어갈 때 둘째 명령어의 인출을 시작하는 식입니다. 세탁기가 돌 때 다음 빨래를 미리 넣어 두는 것과 비슷합니다. 이 방식을 파이프라인이라고 합니다.

GraphQL 요청의 실행 단계

GraphQL은 클라이언트가 원하는 데이터의 모양을 적어 보내면 서버가 그 모양대로 돌려주는 질의 언어입니다. 요청에 적는 데이터 항목 하나하나를 필드라고 합니다.

서버는 요청을 받으면 세 단계를 거칩니다. 먼저 글자를 구조로 바꾸는 파싱입니다. 다음은 검증입니다. 요청이 서버가 정한 스키마에 맞는지 봅니다. 스키마는 서버가 내줄 수 있는 데이터의 종류와 모양을 적은 정의입니다. 마지막이 실행입니다.

flowchart TD
    subgraph CPU
        C1["인출"] --> C2["해독"] --> C3["실행"] --> C4["결과 쓰기"]
    end
    subgraph GraphQL 서버
        G1["파싱"] --> G2["검증"] --> G3["실행<br/>(응답 만들기)"]
    end

두 흐름을 나란히 놓고 보면 모양이 같습니다. 앞의 두 단계는 무엇을 할지 정합니다. 실행은 그것을 수행합니다. 검증에서 걸린 요청은 실행에 들어가지 못합니다. 오류만 돌려받습니다.

실행 단계는 요청에 적힌 필드를 하나씩 채웁니다. 필드마다 값을 구해 오는 함수가 붙어 있습니다. 이 함수를 리졸버라고 합니다. 실행은 요청의 모양을 따라 위 필드부터 아래 필드로 리졸버를 부릅니다. 받은 값은 요청과 같은 모양으로 모아 응답을 만듭니다.

읽기 요청과 쓰기 요청은 도는 순서가 다릅니다. 요청의 맨 바깥 중괄호 바로 안에 적힌 필드를 최상위 필드라고 합니다. 읽기인 쿼리의 최상위 필드는 동시에 값을 구해도 됩니다. 쓰기인 뮤테이션의 최상위 필드는 적힌 순서대로 하나씩 값을 구합니다. 쓰기는 앞의 결과가 뒤에 영향을 주기 때문입니다.

실행 중에 난 오류

필드 하나의 값을 구하는 도중에 오류가 나도 요청 전체가 멈추지는 않습니다. 아래 요청에서 이메일을 가져오는 리졸버가 실패했다고 해 봅니다. 줄 오른쪽 주석은 돌아온 값입니다.

graphql
{
  user(id: 4) {
    name    # "Mark"
    email   # null · 오류 목록에 기록
  }
}

이름은 정상으로 돌아옵니다. 실패한 이메일은 null 로 채워집니다. 무엇이 잘못됐는지는 응답의 errors 목록에 따로 담깁니다. 클라이언트는 받을 수 있는 데이터를 받습니다. 실패한 필드는 그 목록으로 알게 됩니다.

스키마에서 email 을 null 을 허용하지 않는 필드로 정했다면 사정이 다릅니다. email 을 null 로 둘 수 없으니 그 필드를 담은 user 가 통째로 null 이 됩니다. user 도 null 을 허용하지 않으면 한 단계 더 위로 올라갑니다. null 을 허용하는 필드를 만나면 거기서 멈춥니다.

이 올라감은 값을 구해 봐야 알 수 있습니다. 그래서 검증 단계가 아니라 실행 단계에서만 일어납니다.

관련 항목

CPU 에서 실행 앞뒤에 서는 처리 단계

인출 · 해독 · 결과 쓰기 · 파이프라인 · 프로그램 카운터 · 레지스터 · ALU · CPU

요청 처리에서 실행 앞뒤에 서는 처리 단계

파싱 · 검증 · 리졸버 · 직렬화 · 스키마 · 쿼리 · 뮤테이션 · 실행 계획 · GraphQL

프로그램을 실행하는 데 관여하는 구성 요소

프로세스 · 런타임 · 인터프리터 · 컴파일러 · 실행 파일 · 로더

실행 도중에야 드러나는 오류

런타임 오류 · 예외 · 트랩 · 인터럽트

실행 시점과 맞세우는 개념

컴파일 타임 · 로드 타임 · 지연 실행 · 즉시 실행

다른 이름: execution · execute · 수행