PQC Verified

용비어천가와 C언어로 보는 코드의 역사성

by gg582 · 2026-09-28 14:03:06 · 81 views · 4 min read

Table of contents

용비어천가의 한 구절부터 보자

우리가 잘 알고 있는 용비어천가의 구절을 보자.

狄人ㅅ 서〮리예〮 가〮샤〮 狄人이〮 ᄀᆞᆯ외〮어늘〮 岐山 올〯ᄆᆞ〮샴도〮 하ᄂᆞᇙ〮 ᄠᅳ〮디〮시니〮
野人ㅅ 서〮리예〮 가〮샤〮 野人이〮 ᄀᆞᆯ외〮어늘〮 德源 올〯ᄆᆞ〮샴도〮 하ᄂᆞᇙ〮 ᄠᅳ〮디〮시니〮

이렇게 보아서 우리가 이해하기에는 힘들다. 유사한 어원이나 뜻을 가진 요즈음의 우리글로 바꿔 보자.

적인 사이에 가시어, 적인이 갋거늘, 기산 옮으심도 하늘 뜻이시니

야인 사이에 가시어, 야인이 갋거늘, 덕원 옮으심도 하늘 뜻이시니

"옮긴다는 뜻인지, 산이 병처럼 옮기라도 한다는 것인지..." "오랑캐나 야인들이 끽해야 사람을 갋기나 한다니, 너무 순한 것이 아닌가?"

언어 자체의 큰 뜻은 이해가 되지만, 제대로 이해가 되지 않는다.

이 구절을 현대적으로 옮기면?

오랑캐 사이에 가시어, 오랑캐가 침탈하거늘, 기산으로 옮기심도 하늘의 뜻이시니. 야만인 사이에 가시어, 야만인이 침탈하거늘, 덕원으로 옮기심도 하늘의 뜻이시니.

"아하, 오랑캐들의 침탈을 피해 거점을 옮긴다는 정치적 맥락인가?"

정확하다. 고대 중국의 주국(周國)의 주 태왕이 오랑캐의 침탈로 그 재물을 모두 내어 주고 기산으로 거점을 옮겼듯 두만강에 거점을 두던 북방의 여진, 만주족 등의 이민족들의 침탈을 견디던 태조 이성계의 조부 목조 이안사가 함경도 덕원(현재의 원산 인근)으로 피신한 것을 빗대는 구절이다.

즉 건국 초기의 조선 왕조가 자신들의 집권 방식을 "이상적인 국가를 세우기 위한 역성 혁명 과정에서 벌어지는 어쩔 수 없었던 일들"로 정당하다고 주장하는 부분이다.

완전히 맥락이 살아, 역사에 관심이 적은 사람도 맥락을 알 수 있어졌다.

프로그램 역시 마찬가지이다

먼저 이 프로그램을 보자.

main()
{
    char buf[32];
    int n;
    int p1[7];
    char p2[3];

    p1[0] = 'Yo'; p1[1] = 'u '; p1[2] = 'en';
    p1[3] = 'te'; p1[4] = 're'; p1[5] = 'd ';
    p1[6] = 0;

    p2[0] = '!'; p2[1] = '\n'; p2[2] = 0;

    n = read(0, buf, 31);
    if (n <= 0)
        exit(1);

    if (buf[n - 1] == '\n')
        n =- 1;

    write(1, p1, 12);
    write(1, buf, n);
    write(1, p2, 2);

    exit(0);
}

"You entered...까지 읽히고, 직접 read/write로 I/O를 하네? 그래서 실행 결과는?"

프로그램이 뭘 하는지 잘 보이지 않는다.

반쯤만 정리해 보자

unistd.h를 쓰고, 일부 레거시 스타일을 정리한다.

#include <unistd.h>

main()
{
    char buf[32];
    ssize_t n;
    const char p1[] = "You entered ";
    const char p2[] = "!\n";

    n = read(0, buf, sizeof(buf) - 1);
    if (n <= 0)
        return 1;

    if (buf[n - 1] == '\n')
        n--;

    write(1, p1, sizeof(p1) - 1);
    write(1, buf, n);
    write(1, p2, sizeof(p2) - 1);

    return 0;
}

이제 대략적인 의미가 보인다. 뭔가 입력받은 후 그것을 출력한다는 것으로 읽힌다.

하지만 직접 호출하는 read와 write는 여전히 읽기 매우 어렵다.

시대에 맞게 내부 언어를 지켜 가며 현대화해 보자

unistd.h를 이용해서 현대화한다면 맥락을 현대 유닉스 이후로 잡겠다는 소리다. 이 시절이면 원래의 동작을 보존할 이유가 특별히 존재하지 않는다면 우리에게도 친숙한 stdio.h를 써도 된다.

#include <stdio.h>

int main(void) {
    char buf[32];
    int ret = scanf("%31[^\n]", buf);
    if (ret <= 0) return 1;
    printf("You entered %s!\n", buf);
    return 0;
}

입력받은 문자열은 널 문자열 포함 32자여야 하며 읽기 오류를 처리하는구나. 그리고 서식에 맞게 출력하는 거지?

큰 흐름을 보존하면서도 안전 장치 역시 더 직관적이고, 코드 라인 수도 줄었다.

그러나 한 가지 의문이라면...

언어는 역사성이라는 것이 있다. 만약 70년대 메인프레임용 프로그램을 컴퓨터 박물관에서 구동하고 있다면, 그것은 현대 언어로 재작성은 커녕, C나 파스칼의 최신 어법을 도입해서 리팩토링할 필요도 없다. 그 시대의 입력 인터페이스, 하드웨어, 모든 사양들은 그 코드가 나올 수밖에 없는 선택압을 형성하였으며, 그것은 이미 사료로 가치가 있다.

널리 읽히고, 고서점이나 중고 서적 코너에서 심심찮게 보이는 시경, 논어, 맹자를 재해석하는 경우는 많다. 그러나 누구도 "50세에 읽는 칠정산 외편"을 출판하지 않는다.

시경, 논어, 맹자, 플라톤의 국가, 실천이성 비판, 서경과 같은 것들 역시 시대의 선택압과 요구에 맞게 나왔다. 하지만 그것들은 지식이 아닌 지혜를 전하기 때문에 제멋대로 리팩토링당하더라도 핵심적인 결론을 보존할 수 있다.

하지만 칠정산 외편에 현대 천문학의 성과를 대거 도입하여 천문학적 오차를 사실상 0에 가깝게 만든다고 하면 그것은 사료로 가치가 없다. 그 서적이 세종대왕 재위 시절 우리나라의 학자들이 실무적으로 한 선택 자체에 가치를 두기 때문이다.

하지만 이미 MS-DOS 등의 많은 유산은 레트로 게이밍이나 역사학 등 다양한 갈래로 아카이빙되었고, 우리가 현업에서 마주하는 코드들 중 일부는 명백하게 리팩토링이 필요하기도 하다. 한편으론 현업이더라도 2000년대에 설정을 끝내서 유지보수만 진행하는 리눅스 서버에는 OpenJDK 25, Rust 2024와 같은 최첨단 언어가 필요하지 않다.

요즈음 코드를 현대 언어로 재작성하여 더 빠르고 좋게 만드는 열풍이 있다. 이것은 오픈 소스에서 매우 바람직하며 올바른 계승 방식이다. 하지만 이러한 방식을 실무나 연구에서 곧이곧대로 밀어 붙이려고 하면 맥락을 아는 선임의 눈에는 "범위를 넘어가는 일"로 보일 수 있다.

우리는 여전히 코드가 수학적이고 학술적으로 무균실에 가까운 이상이라는 착각에 빠지기 쉽다.

그러나 프로그래밍 언어는 이미 민중 언어처럼 다양한 모습을 가지기 시작하였으며, 설계와 개선은 역사성을 고려하여 진행되어야 한다.

그리고 그 역사성의 의미 가운데에서 강조하고자 한 바가 여전히 우리에게 의미있을 때도 있음을 밝게 알아, 후일에 그르침이 없게 하는 것이 현대인이 사유를 대하는 태도가 되어야 할 것이다. 우리가 아무리 많은 지식을 얻어도 지혜를 얻지 못하면, 지성인은 될지언정 사람 냄새는 나지 않기 때문이다.

UMC의 힙합 가사에서 발췌하며 글을 맺도록 하겠다.

사랑은 삶을 지배하는 동시에 소득의 지배를 받고
삶이 사랑의 지배를 받는 거라는 착각을 심어줬지
우린 지식을 배웠지만 지혜를 얻지는 못했지
알아야했던 모든건, 20년전에 다 배웠거든

Related posts

Back
Report

Comments

No comments yet.