Skip to Content
독학사독학사 3단계프로그래밍언어론07. 언어 평가 기준과 설계 원칙

이번 문서의 목표: 이 문서를 다 읽으면 프로그래밍언어를 가독성·작성 용이성·신뢰성·효율성이라는 네 기준으로 평가할 수 있고, 직교성·단순성 같은 설계 원칙과 형 안전성·정적/동적 검사 같은 트레이드오프가 실제 언어 설계에 어떻게 반영되는지 코드 예제로 설명할 수 있다.

왜 언어를 “평가”해야 하는가

06편에서 언어의 역사와 패러다임을 살펴봤다면, 이번 편의 질문은 “그래서 좋은 언어란 무엇인가”이다. 언어 설계자는 항상 여러 목표 사이에서 선택을 해야 한다. 코드를 읽기 쉽게 만드는 것과 코드를 짧게 쓸 수 있게 만드는 것이 항상 같은 방향을 가리키지는 않고, 오류를 미리 잡아 주는 엄격함과 프로그래머에게 자유를 주는 유연함도 종종 충돌한다. 독학사 시험은 이런 충돌 지점, 즉 “이 설계 선택은 어떤 기준을 얻는 대신 어떤 기준을 희생했는가”를 짚는 비교 문항을 자주 낸다. 이 편에서는 그 판단에 쓰이는 공식적인 평가 기준과 설계 원칙을 정리한다.

언어 평가의 네 기준

언어를 평가하는 기준은 크게 가독성, 작성 용이성, 신뢰성, 효율성 네 가지로 나뉜다. 이 네 기준은 서로 독립적이지 않고, 한 기준을 높이면 다른 기준이 낮아지는 경우가 많다는 점이 시험에서 중요하게 다뤄진다.

가독성(readability)

가독성(readability)이란 다른 사람이 쓴 코드(또는 자신이 예전에 쓴 코드)를 얼마나 쉽게 읽고 이해할 수 있는가를 말한다. 프로그램은 한 번 작성되고 끝나는 것이 아니라 오랜 기간 유지보수되므로, 가독성은 실무에서 가장 중요하게 취급되는 기준 중 하나다. 가독성을 높이는 하위 요소로는 다음이 있다.

  • 단순성(simplicity): 언어가 제공하는 기본 개념의 수가 적고, 그 개념들이 서로 비슷한 일을 서로 다른 문법으로 중복 제공하지 않을수록 가독성이 높아진다.
  • 직교성(orthogonality): 아래 절에서 따로 자세히 다룬다. 기본 개념들을 예외 없이 자유롭게 조합할 수 있을수록 코드를 읽을 때 “이 조합은 되고 저 조합은 안 된다”를 따로 외울 필요가 없어 가독성이 높아진다.
  • 자료형과 구문 설계: 의미 있는 이름의 자료형, 그리고 코드의 구조가 눈에 잘 들어오는 구문(들여쓰기, 예약어 배치 등)도 가독성에 영향을 준다.

쉽게 말하면: 가독성은 “남이 짠 코드를 보고 무슨 뜻인지 금방 알 수 있는가”를 재는 기준이다.

작성 용이성(writability)

작성 용이성(writability)이란 어떤 문제를 해결하는 프로그램을 얼마나 쉽고 빠르게 작성할 수 있는가를 말한다. 가독성이 “읽는 사람” 관점이라면 작성 용이성은 “쓰는 사람” 관점이다. 작성 용이성을 높이는 요소로는 단순성·직교성(가독성과 공유), 그리고 다음 두 가지가 더 있다.

  • 추상화 지원(support for abstraction): 반복되는 절차를 함수로 묶거나, 복잡한 데이터를 하나의 자료형으로 감싸는 등, 세부 사항을 감추고 더 큰 단위로 다룰 수 있는 능력이다.
  • 표현력(expressivity): 같은 논리를 얼마나 적은 코드로, 자연스러운 문법으로 표현할 수 있는가를 말한다.

예를 들어 “1부터 10까지 숫자 중 짝수만 골라 제곱한 목록을 만든다”는 문제를 생각해 보자.

int result[5]; int idx = 0; for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { result[idx] = i * i; idx++; } }
result = [i * i for i in range(1, 11) if i % 2 == 0]

두 코드는 정확히 같은 결과를 만들지만, C 코드는 반복문·조건문·인덱스 변수까지 프로그래머가 하나하나 관리해야 하는 반면, 파이썬의 리스트 컴프리헨션(list comprehension) 구문은 “조건을 만족하는 원소를 변형해 목록으로 만든다”는 의도를 한 줄로 표현한다. 이것이 작성 용이성(그리고 표현력)의 차이다. 다만 표현력이 높다고 항상 가독성까지 함께 높아지는 것은 아니다. 지나치게 압축된 표현은 오히려 낯선 사람에게는 읽기 어려울 수 있다.

쉽게 말하면: 작성 용이성은 “이 언어로 이 문제를 얼마나 빨리, 적은 코드로 풀 수 있는가”를 재는 기준이다.

신뢰성(reliability)

신뢰성(reliability)이란 프로그램이 의도한 대로 정확하게, 예상치 못한 조건에서도 안전하게 동작하는 정도를 말한다. 신뢰성은 그 자체로 독립된 기준이라기보다, 앞의 두 기준과 언어의 다른 기능들이 복합적으로 만들어 내는 결과에 가깝다. 신뢰성에 영향을 주는 요소는 다음과 같다.

  • 형 검사(type checking): 프로그램이 자료형에 맞지 않는 연산(문자열과 정수를 직접 더하는 등)을 시도했을 때 이를 잡아낼 수 있는가.
  • 예외 처리 지원: 실행 중 발생하는 예외 상황(05편 참고)을 언어가 얼마나 체계적으로 처리할 수 있게 해 주는가.
  • 별칭(aliasing): 서로 다른 이름(변수)이 같은 메모리 위치를 가리킬 수 있는 성질이다. 별칭이 자유롭게 허용될수록 한 이름을 통한 변경이 다른 이름을 통해 예상치 못하게 관찰되어 오류를 만들기 쉬워지므로, 별칭을 제한하는 언어 설계는 신뢰성을 높이는 방향으로 작용한다.
  • 가독성과 작성 용이성 자체도 신뢰성에 기여한다. 코드가 읽기 쉬우면 오류를 발견하기 쉽고, 쓰기 쉬우면 실수로 잘못 쓸 가능성이 줄어든다.

쉽게 말하면: 신뢰성은 “이 프로그램이 실제로 믿고 쓸 수 있을 만큼 정확하고 안전하게 동작하는가”를 재는 기준이며, 형 검사·예외 처리·별칭 제한 같은 장치들이 이를 뒷받침한다.

효율성(efficiency)

효율성(efficiency)이란 프로그램이 실행되는 데 걸리는 시간과 사용하는 메모리 같은 자원을 얼마나 적게 쓰는가를 말한다. 효율성은 컴파일 시점의 효율(컴파일러가 얼마나 빠르고 최적화된 기계어를 만들어 내는가)과 실행 시점의 효율(실제로 프로그램이 도는 속도)로 나누어 볼 수 있다. 흥미로운 점은 효율성이 종종 가독성·신뢰성과 충돌한다는 것이다. 예를 들어 실행 중 배열의 경계를 벗어난 접근인지 매번 검사하면 신뢰성은 높아지지만 그 검사에 드는 시간만큼 효율성은 낮아진다. 반대로 이런 검사를 아예 생략하면 실행은 빨라지지만 오류를 잡아낼 기회를 잃어 신뢰성이 낮아진다.

자주 틀리는 점: “가장 좋은 언어는 네 기준을 모두 최고 수준으로 만족시키는 언어다”라는 생각은 시험에서 틀린 방향이다. 실제로는 네 기준이 서로 트레이드오프(trade-off, 하나를 얻으면 다른 하나를 잃는 관계) 관계에 있는 경우가 많고, 언어 설계는 “어떤 용도에 어떤 기준을 우선할 것인가”를 선택하는 과정이다.

직교성(orthogonality)과 단순성

직교성(orthogonality)이란 언어가 제공하는 소수의 기본 개념(연산자, 자료형, 제어 구조 등)을 서로 예외 없이 자유롭게 조합할 수 있는 성질을 말한다. 원래 직교(orthogonal)라는 말은 수학에서 “서로 독립적이어서 하나를 바꿔도 다른 하나에 영향을 주지 않는 성질”(예: 서로 수직인 두 축)을 가리키는데, 언어 설계에서는 이 뜻을 “기본 요소들이 서로 간섭 없이 자유롭게 조합될 수 있는가”로 가져와 쓴다.

직교성이 낮다는 것은 “이 조합은 되지만 저 조합은 안 된다”는 예외가 많다는 뜻이다. 예를 들어 C 언어에서는 함수가 배열을 값으로 직접 반환할 수 없다. 배열이 필요하면 배열을 가리키는 포인터를 반환하거나, 배열을 구조체(struct)로 감싸서 반환해야 한다.

/* 컴파일되지 않는다: 함수가 배열을 값으로 직접 반환할 수 없다 */ int[] makeArray() { int arr[3] = {1, 2, 3}; return arr; }
/* 자바에서는 배열도 객체이므로 함수가 배열을 값(참조)으로 그대로 반환할 수 있다 */ public static int[] makeArray() { int[] arr = {1, 2, 3}; return arr; }

C에서는 “함수가 반환할 수 있는 것”이라는 하나의 규칙 안에 “기본형은 되지만 배열은 안 된다”는 예외가 끼어 있다. 반면 자바에서는 배열도 다른 값들과 마찬가지로 하나의 참조값으로 취급되기 때문에, “함수는 값을 반환한다”는 하나의 규칙이 배열을 포함한 모든 자료형에 예외 없이 적용된다. 이 예제에서 자바 쪽이 배열이라는 자료형과 “반환값”이라는 기능을 더 직교적으로 조합하고 있다고 말할 수 있다.

쉽게 말하면: 직교성이 높다는 것은 “이 규칙에 예외가 없다”는 뜻이고, 직교성이 낮다는 것은 “이런 경우엔 되고 저런 경우엔 안 된다”를 하나하나 외워야 한다는 뜻이다.

단순성(simplicity)은 언어가 제공하는 기본 개념의 가짓수 자체를 적게 유지하는 성질이다. 직교성과 단순성은 자주 같이 언급되지만 서로 다른 개념이다. 직교성은 “가진 개념들을 얼마나 자유롭게 섞을 수 있는가”를 보고, 단순성은 “애초에 개념의 가짓수가 얼마나 적은가”를 본다. 그런데 이 둘은 서로 충돌할 수도 있다. 만약 어떤 언어가 완전한 직교성을 추구해서 모든 기본 개념을 모든 문맥에서 조합할 수 있게 허용하면, 그 조합의 가짓수 자체가 폭발적으로 늘어나 오히려 언어가 복잡해 보이고 배우기 어려워질 수 있다. 그래서 실제 언어 설계는 직교성과 단순성 사이에서도 균형을 찾는다.

자주 틀리는 점: 직교성과 단순성을 같은 개념으로 혼동하는 문제가 자주 나온다. 직교성은 “조합의 자유로움과 일관성”이고 단순성은 “개념 가짓수의 적음”이며, 직교성을 지나치게 높이면 오히려 단순성이 떨어질 수 있다는 관계까지 함께 기억해야 한다.

설계 트레이드오프 1 — 형 안전성 대 유연성

형 안전성(type safety)이 높은 언어는 자료형이 맞지 않는 연산을 컴파일 시점이나 실행 시점에 확실히 차단한다. 반대로 형 안전성을 느슨하게 유지하는 언어는 프로그래머에게 더 많은 자유(유연성, flexibility)를 주는 대신, 자료형이 맞지 않아 생기는 오류를 스스로 책임지게 만든다.

int main() { int number = 65; char letter = number; /* 정수를 문자로 암묵적 변환. 컴파일러가 경고 없이 허용 */ int *ptr = (int *)"문자열"; /* 문자열 포인터를 정수 포인터로 강제 변환. 위험하지만 컴파일됨 */ return 0; }
public class TypeSafetyExample { public static void main(String[] args) { int number = 65; // char letter = number; // 컴파일 오류: int를 char로 암묵적 변환 불가(명시적 형변환 필요) char letter = (char) number; // 명시적으로 형변환해야만 허용됨 System.out.println(letter); } }
A

C는 정수와 문자 사이의 암묵적 변환을 폭넓게 허용하고, 심지어 서로 다른 포인터 자료형 사이의 강제 변환도 경고 수준으로만 처리하는 경우가 많다. 이는 하드웨어에 가까운 유연한 제어를 가능하게 하지만, 실수로 잘못된 자료형끼리 연산해도 컴파일러가 이를 걸러 주지 못해 신뢰성이 낮아지는 대가를 치른다. 반면 자바는 intchar로 대입하려는 시도 자체를 컴파일 오류로 막고, 반드시 (char)처럼 명시적으로 형변환하도록 강제한다. 프로그래머의 자유는 줄어들지만, 실수로 자료형을 잘못 섞어 쓰는 오류를 컴파일 시점에 미리 잡아 주므로 신뢰성은 높아진다.

쉽게 말하면: 형 안전성을 높이면 실수를 미리 막아 주는 대신 프로그래머가 할 수 있는 일이 줄어들고, 형 안전성을 낮추면 더 자유롭게 쓸 수 있는 대신 실수를 스스로 책임져야 한다.

설계 트레이드오프 2 — 정적 검사 대 동적 검사

정적 검사(static checking)는 프로그램을 실행하기 전, 즉 컴파일 시점에 오류 가능성을 미리 찾아내는 것이고, 동적 검사(dynamic checking)는 프로그램이 실제로 실행되는 도중에 오류를 찾아내는 것이다. 이 구분은 10편(형 시스템)에서 정적 타입 언어와 동적 타입 언어를 비교할 때 다시 자세히 다루므로, 여기서는 “언제 검사하는가”라는 설계 트레이드오프 관점만 짚는다.

public class StaticCheckExample { public static void main(String[] args) { int number = 10; // number = "문자열"; // 컴파일 오류: 자바는 컴파일 시점에 자료형 불일치를 잡아낸다 System.out.println(number + 5); } }
15
number = 10 number = "문자열" # 컴파일 시점에는 아무 문제 없이 통과된다 print(number + 5) # 이 줄이 실제로 실행되어야 비로소 오류가 발생한다
Traceback (most recent call last): File "example.py", line 3, in <module> print(number + 5) TypeError: can only concatenate str (not "int") to str

자바 코드에서 정수형 변수 number에 문자열을 대입하려는 시도는 컴파일 단계에서 바로 오류로 걸러진다. 반면 파이썬은 변수의 자료형을 실행 중에 값에 따라 유동적으로 결정하는 동적 타입 언어이므로, number에 문자열을 다시 대입하는 코드 자체는 아무 문제 없이 실행된다. 문제는 그다음 줄에서 문자열과 정수를 더하려고 시도하는 순간, 즉 그 코드가 실제로 실행되는 시점에야 비로소 드러난다.

정적 검사는 오류를 프로그램을 배포하기 전에 미리 잡아내므로 신뢰성 확보에 유리하지만, 프로그래머가 자료형을 매번 명시해야 하는 등 작성 용이성이 다소 떨어질 수 있다. 동적 검사는 자료형 선언 없이 자유롭게 코드를 쓸 수 있어 작성 용이성이 높지만, 자료형 오류가 실제 실행 경로를 타기 전까지는 드러나지 않아 신뢰성 확보에는 더 많은 테스트가 필요하다.

쉽게 말하면: 정적 검사는 “출발하기 전에 지도를 다 확인하는 것”이고, 동적 검사는 “일단 출발해서 길을 가다가 막힌 곳에서만 문제를 아는 것”이다.

자주 틀리는 점 정리

  • 네 평가 기준이 서로 독립적이라고 오해하는 실수: 가독성·작성 용이성·신뢰성·효율성은 서로 얽혀 있으며, 한 기준을 높이면 다른 기준이 낮아지는 트레이드오프 관계가 흔하다.
  • 직교성과 단순성을 같은 말로 쓰는 실수: 직교성은 조합의 자유로움과 일관성, 단순성은 개념 가짓수의 적음을 가리키는 서로 다른 기준이다.
  • 직교성이 높을수록 무조건 좋다고 단정하는 실수: 직교성을 극단적으로 추구하면 조합의 가짓수가 폭발해 오히려 단순성과 가독성이 떨어질 수 있다.
  • 형 안전성이 낮은 언어를 무조건 나쁜 언어로 평가하는 실수: 형 안전성을 낮추는 대신 얻는 유연성(하드웨어 근접 제어 등)이 필요한 도메인(시스템 프로그래밍 등)도 있으므로, “낮은 형 안전성 = 나쁜 설계”로 단정할 수 없다.
  • 정적 검사와 동적 검사를 “옳고 그름”의 문제로 오해하는 실수: 두 방식은 우열이 아니라 신뢰성과 작성 용이성 사이의 서로 다른 선택이다.

핵심 정리

  • 언어 평가 기준은 가독성, 작성 용이성, 신뢰성, 효율성 네 가지이며, 서로 트레이드오프 관계에 있다.
  • 직교성은 기본 개념을 예외 없이 자유롭게 조합할 수 있는 성질이고, 단순성은 개념 가짓수 자체가 적은 성질이다. 둘은 서로 다른 기준이며 때로 충돌한다.
  • 형 안전성을 높이면 오류를 미리 차단하는 대신 유연성이 줄고, 낮추면 유연성이 느는 대신 오류를 프로그래머가 책임져야 한다.
  • 정적 검사는 컴파일 시점에, 동적 검사는 실행 시점에 오류를 찾아내며, 신뢰성과 작성 용이성 사이의 트레이드오프를 만든다.

마무리 복습

문제 14지선다
프로그래밍언어 평가의 네 가지 기준에 해당하지 않는 것은?
문제 24지선다
직교성(orthogonality)에 대한 설명으로 가장 적절한 것은?
문제 34지선다
C 언어에서 함수가 배열을 값으로 직접 반환할 수 없어 포인터나 구조체로 감싸야 하는 상황은 어떤 개념의 예로 볼 수 있는가?
문제 44지선다
형 안전성(type safety)과 유연성(flexibility)의 관계에 대한 설명으로 옳은 것은?
문제 54지선다
정적 검사(static checking)와 동적 검사(dynamic checking)에 대한 설명으로 옳지 않은 것은?
문제 64지선다
언어 평가 기준 중 신뢰성(reliability)에 영향을 주는 요소로 볼 수 없는 것은?

참고 자료

Last updated on