사전 프로그래밍 언어
개념

프로그래밍 언어

gabury1고친 사람 github-actions[bot]

프로그래밍 언어는 컴퓨터에게 시킬 일을 사람이 읽고 쓸 수 있는 글로 적게 해 줍니다. 적어 둔 글은 기계가 실행할 수 있는 형태로 옮겨져 돌아갑니다. 어떤 글이 무슨 뜻인지는 미리 정해져 있습니다. 같은 코드를 누가 읽어도 같은 동작으로 읽힙니다.

쉽고 빠른 이해

프로그래밍 언어는 사람이 적은 글을 컴퓨터가 할 일로 바꿔 주는 약속입니다. 총액 = 가격 * 개수 라고 적어 두면 곱셈이 돌고 그 결과가 총액이라는 이름에 담깁니다.

기계가 직접 알아듣는 것은 숫자로 된 명령뿐입니다. 그것을 손으로 늘어놓으면 한 줄만 끼워 넣어도 뒤가 전부 밀립니다. 기계가 달라지면 처음부터 다시 적어야 합니다.

  1. 언어가 정해 둔 낱말과 문장 모양으로 코드를 적습니다
  2. 도구가 그 코드를 읽어 문법에 맞는지 보고 기계가 실행할 형태로 옮깁니다
  3. 옮긴 것을 실행하면 적어 둔 순서대로 계산이 돕니다

대가가 있습니다. 언어가 미리 정해 둔 틀 밖의 일은 적기 어렵습니다. 사람이 적은 글과 기계가 하는 일 사이에는 한 겹이 낍니다. 느려진 대목을 찾을 때는 그 겹까지 같이 들여다봐야 합니다.

상세

요리법 카드를 떠올려 봅니다. 재료와 순서를 정해진 말로 적어 두면, 그 카드를 받은 사람은 누구든 같은 요리를 만들어 냅니다. 적는 말이 미리 약속돼 있어서 옆에서 거들지 않아도 통합니다.

프로그래밍 언어는 컴퓨터에게 시킬 일을 그렇게 적는 표기 체계입니다. 무엇을 적을 수 있는지와, 그렇게 적힌 것이 무슨 일을 하는지가 함께 정해져 있습니다.

아래 세 줄은 값을 둘 두고 곱한 뒤 그 결과에 이름을 붙입니다.

가격 = 1500
개수 = 2
총액 = 가격 * 개수      // 3000

이만한 일은 어느 언어로 적어도 모양이 크게 다르지 않습니다. 언어마다 갈리는 것은 값의 종류를 언제 확인해 주는지, 다 쓴 메모리를 누가 치우는지 같은 약속입니다.

기계어를 직접 적지 않는 이유

프로세서가 알아듣는 것은 정해진 숫자 명령, 곧 기계어뿐입니다. 그 숫자를 사람이 직접 늘어놓아도 프로그램은 돕니다. 다만 중간에 한 줄을 끼워 넣으면 뒤따르는 번호가 전부 밀립니다.

이름이 없다는 것도 부담입니다. 어떤 값이 메모리 어디에 담겼는지는 사람이 기억해야 합니다. 그 기억이 어긋나면 엉뚱한 값을 읽습니다.

프로그래밍 언어는 그 둘을 대신 맡습니다. 메모리 주소 대신 이름으로 가리키게 합니다. 명령을 늘어놓는 순서는 도구가 맞춥니다. 그래서 사람은 무엇을 시킬지만 적습니다.

문법과 의미

언어는 두 겹으로 정해집니다. 문법은 어떤 글자 배열을 그 언어의 코드로 인정할지를 정합니다. 의미는 그렇게 인정된 코드가 무슨 일을 하는지를 정합니다.

둘이 갈려 있어서 문법에 맞는데도 말이 안 되는 코드가 나옵니다. 숫자에서 글자를 빼라고 적으면 문장 모양은 멀쩡하지만 시킬 일이 정해지지 않습니다.

그래서 도구는 두 번 걸러 냅니다. 먼저 문장 모양을 보고, 그다음 뜻이 서는지 봅니다. 앞에서 걸리는 것이 구문 오류입니다. 뒤에서 걸리는 것은 뜻이 어긋난 코드입니다.

코드가 실행되기까지

사람이 적은 코드는 그대로 실행되지 않습니다. 기계가 실행할 수 있는 형태로 한 번 옮겨집니다. 옮기는 시점을 언제로 잡느냐에 따라 크게 두 갈래로 갈립니다.

미리 옮겨 두는 방식은 코드 전체를 훑어 실행 형태로 바꿔 둡니다. 그 일을 하는 도구가 컴파일러입니다.

읽으면서 바로 실행하는 방식은 코드를 한 조각씩 읽어 그 뜻대로 곧장 수행합니다. 그 도구가 인터프리터입니다. 앞에서 코드를 걸러 내고 옮겨 준다고 한 도구가 이 둘입니다.

둘을 섞는 언어도 많습니다. 이 방식은 코드를 중간 형태로 한 번 옮겨 둡니다. 그 중간 형태가 바이트코드입니다. 사람이 적은 글과 기계어 사이에 놓인다는 뜻입니다.

바이트코드를 읽어 실행하는 것은 가상 머신입니다. 진짜 기계가 아니라 바이트코드를 알아듣는 흉내 기계입니다. 앞의 인터프리터는 사람이 적은 코드를 바로 읽습니다. 가상 머신은 이미 한 번 옮겨 둔 바이트코드를 읽습니다.

언어가 어느 한 갈래에 묶여 있지는 않습니다. 같은 언어를 두 방식으로 굴리기도 합니다. 세 갈래를 한자리에 놓으면 이렇습니다. 어느 길로 가든 마지막에는 기계가 실행합니다.

flowchart TD
    S["사람이 적은 코드"]
    S --> C["미리 옮겨 두는 방식"]
    S --> M["둘을 섞는 방식"]
    S --> I["읽으면서 바로 실행하는 방식"]
    C --> B["옮겨 둔 실행 형태"]
    M --> Y["바이트코드"]
    Y --> V["가상 머신"]
    B --> R["기계가 실행"]
    V --> R
    I --> R

언어마다 갈리는 약속

언어를 고를 때 갈리는 것은 문장 모양보다 아래 약속들입니다. 같은 일을 적어도 언제 무엇을 확인해 주는지가 다릅니다.

약속 한쪽 다른 쪽
값의 종류를 언제 확인하나 실행하기 전에 미리 실행하다가 그때그때
다 쓴 메모리를 누가 치우나 언어가 알아서 사람이 직접
무엇을 기본 단위로 삼나 명령을 차례로 늘어놓기 값을 받아 값을 내는 함수

첫째 행은 잘못을 언제 알게 되느냐를 가릅니다. 실행 전에 확인하는 쪽은 돌려 보기도 전에 어긋남을 알려 줍니다. 실행 중에 확인하는 쪽은 그 코드를 지나갈 때 알려 줍니다.

메모리를 언어가 치워 주는 쪽에서는 쓰지 않는 값을 찾아 정리하는 가비지 컬렉션이 뒤에서 돕니다. 사람이 치우는 쪽에서는 언제 치울지를 코드가 직접 적습니다.

셋째 행은 코드를 무엇으로 쌓느냐를 가릅니다. 한쪽은 시킬 일을 차례대로 적어 내려갑니다. 다른 쪽은 값을 넣으면 값이 나오는 덩어리를 만들어 그것들을 엮습니다.

한 겹을 얹는 대가

사람이 적은 글과 기계가 하는 일 사이에 한 겹이 끼면 편해지는 대신 안 보이는 것이 생깁니다. 코드 한 줄이 명령 몇 개가 되는지는 언어가 정합니다. 그 결정을 사람이 매번 들여다보지는 않습니다.

그래서 느려진 대목을 찾을 때는 적어 둔 코드만 봐서는 안 됩니다. 옮겨진 실행 형태와, 프로그램이 도는 동안 뒤에서 메모리와 값을 챙겨 주는 런타임까지 같이 봐야 원인이 잡힙니다.

언어가 정해 둔 틀도 대가입니다. 언어가 다루지 않기로 한 것은 그 언어 안에서 적을 수 없습니다. 그때는 바깥의 도움을 빌립니다.

관련 항목

코드를 실행 형태로 옮기는 도구

컴파일러 · 인터프리터 · 어셈블러 · 링커 · 로더 · 가상 머신 · 바이트코드 · 런타임 · 기계어

프로그래밍 언어를 이루는 규칙 층

구문 · 의미론 · 형식 문법 · 타입 시스템 · 예약어 · 식별자 · 연산자 · 스코프 · 추상 구문 트리

언어가 프로그램을 나누는 단위

변수 · 함수 · 클래스 · 모듈 · 패키지 · 네임스페이스 · 표준 라이브러리 · 제어 흐름 · 예외

언어를 가르는 설계 축

정적 타입 · 동적 타입 · 강한 타입 · 약한 타입 · 가비지 컬렉션 · 수동 메모리 관리 · 메모리 안전성 · 튜링 완전성 · 동시성

언어가 따르는 프로그래밍 방식

절차적 프로그래밍 · 객체 지향 프로그래밍 · 함수형 프로그래밍 · 명령형 프로그래밍 · 선언형 프로그래밍 · 메타프로그래밍

두루 쓰이는 프로그래밍 언어

C · C++ · Java · Python · JavaScript · Go · Rust · Kotlin · Ruby

프로그램 아닌 것을 적는 표기 언어

SQL · HTML · CSS · 정규 표현식 · 셸 스크립트 · JSON · XML

언어로 적은 코드를 다루는 도구와 결과물

빌드 · 패키지 매니저 · 정적 분석 · 디버거 · 프로파일러 · 실행 파일 · 공유 라이브러리 · 통합 개발 환경

언어 위에서 자주 나는 오류

구문 오류 · 타입 오류 · 널 참조 · 정의되지 않은 동작 · 메모리 누수 · 버퍼 오버플로 · 세그멘테이션 폴트

프로그래밍 언어가 속하는 상위 분류

개발 · 컴퓨터 과학 · 시스템 프로그래밍 · 소프트웨어 아키텍처

다른 이름: programming language · 프로그래밍언어