나무모에 미러 (일반/어두운 화면)
최근 수정 시각 : 2024-11-22 04:28:04

스파게티 코드

1. 개요

프로그래밍에서 기술적 부채에 해당하는 분류로, 컴퓨터 프로그램의 흐름이 복잡하게 뒤엉킨 모습스파게티가 엉킨 모습에 비유한 표현이다.

스파게티 코드는 겉으로는 비교적 제대로 작동하는 것처럼 보이나, 가독성이 많이 떨어지고 유지보수가 매우 어려워진다. 작동 방식을 변경하거나 버그를 찾거나 개선하는 등 코드를 수정하는 모든 작업에 방해가 된다. 특히 OOP에서 상속(프로그래밍) 계층이 너무 많아 생긴 스파게티 코드는 따로 라자냐 코드라고도 한다. # 물론 OOP에도 고전적인 스파게티 코드가 생길 수 있다.

스파게티 코드는 프로그래밍 초보자들이 많이 작성한다. 심각한 경우 '수정할 바에야 다시 만드는 게 낫다'는 농담을 하기도 한다. 실제로 규모가 지나치게 커진 스파게티 코드는 처음부터 다시 만드는 것이 유일한 해결책이다. PC게임을 모바일로 포팅할 경우에는 스파게티 코드가 아니더라도 처음부터 다시 만드는 것이 경제적일 때가 많다. 아래의 실제 사례 문단 참고.

파일:spaghetti_example.gif

2. 발생 원인

보통 계획 없이 바로 코딩을 시작하거나 요구사항이 원래 기획한 범위를 크게 벗어날 때, 혹은 프로그래머알고리즘 구현 실력이 지나치게 떨어질 때 주로 나타난다. 중간에 인원이 바뀌면서 전임자가 짜 놓은 코드의 인수인계가 잘 되지 않고 이해도가 떨어질 경우 발행하기 쉽다. 발적화도 덤으로 나타나는 경우가 많지만, 스파게티 소스 코드라고 모두 실제 작동에서 비효율적인 것은 아니다.
#include <stdbool.h>

int main(void) {
bool a = true;
bool b = false;
bool c = true;
int condition = ((int)a << 2) | ((int)b << 1) | ((int)c);
printf("%d\n", condition); // 4+1 = 5
if(condition == 0x0005) {
printf("[a, b, c] == [T ,F, T]\n");
}
}
}}}
fs.readdir(source, function(err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function(filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function(err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function(width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(destination + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
});
}}}

3. 실제 사례

3.1. 넷스케이프

웹 브라우저 중 넷스케이프가 이 상태였다. 1990년대 후반부터 2000년대 전반까지 벌어진 웹 브라우저 전쟁에서 넷스케이프는 코드 정리 이런 것 없이 급하게 기능을 추가했고, 그 바람에 코드가 점점 꼬여가며 헬게이트를 열었다.

넷스케이프가 오픈소스화된 이후 모질라 재단이 이 소스 코드로 브라우저를 만들려고 했다가 포기하고 2년 동안 아예 백지에서 출발해 게코 엔진을 만든 것은 유명한 일화다. 이 엔진으로 나온 것이 모질라 파이어폭스다.

3.2. 리그 오브 레전드

리그 오브 레전드는 작은 개발 규모에서 시작해 10년 넘게 서비스하는 대형 게임인 만큼, 게임 전체가 스파게티 코드 덩어리이다. 영미권에서는 아예 라이엇 게임즈를 스파게티 전문점으로 왜곡하는 밈이 있을 정도다.

겉으로는 비슷한 스킬들이 속은 전부 다르게 설계되어 있어, 새로운 매커니즘이 추가될 때마다 기존 메커니즘과의 정상적인 상호작용이 불가능하다. 유저들에게도 알려진 가장 유명한 예시로 풀숲에 있을때만 원거리로 전환되던 렝가의 패시브, 상대방의 궁극기를 뺏어오는 사일러스, 그리고 상대 처치시 일정시간 적 챔피언으로 빙의할 수 있는 비에고가 있다. 개발진들은 이렇게 새로 만드는 메커니즘은 임시방편으로 예외 케이스들을 일일이 처리하는 식으로 버그를 수정한다. 문제는 이렇게 하면 당장은 해당 버그를 피할 수 있지만 땜빵이라 근본적인 문제를 해결하지 못했기 때문에 언제든지 비슷한 문제가 또 일어날 수 있으며, 코드가 길어지며 가독성 또한 나빠진다.

특히 렝가는 캐릭터의 스킬셋을 구성하는 코드가 심하게 꼬여 있어서 몇 년째 끊임없이 버그를 수정하는데도 계속해서 버그가 나오고 있다. 2021년 기준으로 발견된 버그만 해도 50개에 달한다. 그 중 게임 플레이에 영향을 미치지 않는 버그는 단 3개로, 나머지 버그들은 죄다 인게임에 치명적인 영향을 줬던 버그들이다. 그 뒤를 사일러스비에고가 이으며 현재까지도 임시방편식 버그 수정을 거듭하고 있다.

결국 모바일 버전은 PC 버전 코드의 사용을 포기하고 유니티 엔진으로 처음부터 다시 만들었다. 그 결과물이 바로 리그 오브 레전드: 와일드 리프트. 그 때문인지 PC 버전에서 버그의 화신으로 이름을 날리던 렝가도 와일드 리프트에서는 딱히 큰 문제를 일으키지 않았다.

스파게티 코드가 악영향을 준 실제 사례가 바로 전략적 팀 전투의 개발이다. 원래 개발 계획은 리그 오브 레전드의 리소스와 코드를 적당히 재활용해서 구현하는 것이었는데, 원본이 심각한 스파게티 코드인 탓에 거의 처음부터 만들어야 했다고 한다. 결국 다 만들고 보니 구조가 완전히 다른 두 개의 게임을 하나의 클라이언트에 합쳐서 서비스하게 되었는데, 당연히 불어난 용량으로 인해 클라이언트의 최적화는 산으로 가며 렉과 버그를 뿜어내기 시작했다. 전략적 팀 전투의 출시로 두 개의 게임을 감당하느라 불안정해진 클라이언트를 고치는 데에만 2년 넘게 걸렸다. 근데 이 클라이언트조차도 구 클라이언트의 문제점[5]을 해결하기 위해 교체된 신 클라이언트라는 것. 말 그대로 총체적 난국이다.

3.3. 마인크래프트 자바 에디션

소수 인원이 개발하는 인디 게임은 보통 게임 개발 초심자들이 많고 코드를 다듬을 인원이 부족하다 보니 코드가 스파게티인 경우가 많은데, 마인크래프트 개발 초기의 모장도 예외가 아니었다 보니 마인크래프트 자바 에디션은 스파게티 코드로 악명이 높다. 특히 개발자였던 마르쿠스 페르손(노치)의 프로그래밍 역량이 떨어지다 보니 그냥 어떻게든 돌아가기만 하면 된다는 마인드로 주석 처리도 부실하게 이리저리 꼬인 구조였다고 한다.

그 예시로 몹의 인공지능은 버그로 인해 무한 루프가 자주 일어났는데[6], 노치는 원인을 찾아 해결하는 대신 그냥 루프한 횟수를 세서 일정 수가 넘어가면 그냥 인공지능을 멈춰버렸다. 그래서 몹들이 가끔 아무 이유 없이 멈춰서곤 했다. 또한 유저들이 벽에 붙인 횃불은 빛이 나는데 왜 손에 들린 횃불에서는 빛이 나지 않는지에 대해 묻자 노치는 구현이 불가능하다는 입장을 보였으나, 머지않아 모드 팀들이 손에 들린 채로 빛나는 횃불을 구현하면서 노치의 역량 문제라는 것을 까발려버렸다. 이 때문에 버전업을 할 때마다 코드를 대폭 갈아치워서 이전 버전의 모드는 대부분 호환이 되지 않았고, 대규모 업데이트인 1.16 버전 업데이트에 대비하기 위해 1.15 버전의 컨텐츠 일부를 희생해야 했다. 지금도 옵티파인 또는 각종 최적화 모드팩들을 적용하면 프레임이 몇 배는 오를 정도로, Java 언어 자체의 한계를 감안해도 기본적인 최적화 상태가 좋지 않다. 버전이 올라갈수록 개선되고는 있지만, 마인크래프트에 투입되는 인력이 많지 않아 새 버전 컨텐츠 구현과 심도 있는 최적화를 병행하긴 버거운 모양.

그래서 마이크로소프트가 모장을 인수하고 C++로 다시 개발한 베드락 에디션은 아예 소스 코드를 완전히 갈아엎어 리버스 엔지니어링에 가깝게 새로 만드는 수준의 최적화를 진행했는데, 나온 결과물은 자바 에디션과 비교를 거부하는 최적화를 보여준다. 상술한 유일한 해결법인 '처음부터 다시 만들기'를 실제로 사용한 사례. 베드락 에디션의 전신인 포켓 에디션은 당시 너무 무거웠던 자바 에디션의 모든 기능을 이식받을 수 없었지만, 인수 이후 최적화를 거친 베드락 에디션은 자바 에디션의 모든 기능을 크로스 플랫폼으로 구현 가능할 정도로 최적화되어 양 플랫폼을 통합할 수 있게 되었다. 심지어는 그러고도 자바 에디션보다 더욱 빠르다. 이 과정에서 그나마 다행인 건 마인크래프트의 구현 난이도가 중견 프로그래머라면 어느 정도 시간을 들여서 비슷하게 만들 수 있을 정도로 쉬웠기 때문에 마이크로소프트가 단기간 내에 재설계를 진행할 수 있었다는 점이다.

3.4. 던전앤파이터

온라인 게임 던전앤파이터도 스파게티 코드로 악명이 높았다. 개발사 네오플도 도대체 이걸 어떻게 건드릴지 감도 안 온다고 우는 소리를 할 정도로 코드가 꼬일대로 꼬여버려서 규모 있는 패치 직후 패치 내용과는 전혀 상관 없는 곳에서 버그가 발생하는게 일상이며, 게임 플레이에 치명적이지 않으면서 해결 불가능한 버그는 손을 놔버리는 경우가 타 게임에 비해 많은 편이다. 어떠한 버그는 도저히 해결 할 수가 없어서 우회하는 방법을 만들어두었다.[7] 한 전문가가 던파의 소스 코드를 열어보고 이렇게 코드가 꼬여있는데 도대체 어떻게 게임이 돌아가는지 모르겠다고 감탄할 정도로 배배 꼬였다.

유독 던파의 코드 꼬임이 악명 높았던 이유는 최초의 던파를 만든 프로그래머들의 실력이 그닥 좋지 않아서라고도 하지만[8] 진짜 원인은 플레이어 캐릭터의 대미지 강화 수단이 지나치게 많았던게 중요 원인으로 보고 있다. 던전앤파이터/데미지 계산 공식 문서를 가보면 알겠지만 시즌 8을 시작하며 대대적으로 삭제한 대미지 강화 수단을 보면 정말 어질어질하게 많다. 대표적으로 유명했던 추가 대미지, 증가 대미지, 크리티컬 대미지 증가 등등 한두개가 아니다보니 이것들끼리 충돌을 일으켰을 가능성이 높다. 게다가 더 이전엔 특정 몬스터 타입을 공격시 추가 피해 혹은 공격력 증가 같은 것도 있었고 크로니클 장비로 특정 스킬이 달라지는 것도 있었으니 엉키지 않는게 이상할 정도였다.

이렇게 수습이 불가능할 정도로 코드가 꼬여있기 때문에 유명한 게임이라면 한번씩 등장하는 프리서버조차 만들어지지 않고 있다가 대만 서버가 서비스 종료 후 유출된 데이터를 기반으로 간신히 만들어졌다. 이미 있는 서버 데이터를 베이스로 하지 않으면 프리서버를 만들지 못할 정도로 코드 꼬임이 심각하단 이야기로, 리버스 엔지니어링조차 불가능해서 코드를 완전히 이해하지 못한 상태로 서버를 연 셈이니 프리서버의 업데이트와 유지보수도 험난해 보이는 건 안 봐도 비디오.

게다가 더미 데이터의 양이 너무 많은 것도 한 몫하고 있다. 던파는 특이하게도 게임을 설치할 때 실행에 필요한 이미지와 사운드 파일을 npk 확장자로 압축하고 음악 파일은 ogg 확장자로 설정해 컴퓨터에 설치하는 식인데 당장 음악 폴더로 가보면 이제는 사용을 안하는 이벤트 용으로 만든 음악도 그대로 남아있다. 이러니 더 꼬이는지도 모른다.

그래도 2023년 기준으로 스파게티 코드 문제는 상당 부분이 해소되었는데, 과거 김성욱 디렉터 시절부터 꾸준히 개선을 거듭한 결과 상당수의 버그들이 제거되고 로딩 속도도 빨라지게 되었다. 지금도 수시로 낡은 리소스를 수정하고 개편해서 많이 나아졌다. 한 예로 2023년 7월 6일 무려 오후 2시까지 점검 연장이 되었던 업데이트가 낡은 코드 구조를 리팩토링 하다가 무점검 패치로 해결 불가능한 수준의 문제가 있어 점검 시간을 연장해 오후 2시까지 한 것이다. 그래도 더이상 스파게티 코드에 시달리기는 싫었는지, 던전앤파이터 모바일은 유니티 엔진을 기반으로 개발되었다.

3.5. 마비노기

마비노기도 약 20년의 역사가 있는 게임인 만큼 스파게티 코드가 심하다. 애초에 개인이 자체제작한 엔진인 플레이오네 엔진을 아직까지도 쓰고 있는 것부터가 문제. 이 엔진은 마비노기에서만 사용하는 중인데, 이걸 따로 배우는 사람도 없고, 이걸 쓸 줄 아는 사람도 팀에서 점점 떠나가다보니 유지보수를 하는 것조차도 쉽지 않다. 마비노기에는 위치렉을 비롯해 아직도 해결이 안 된 버그가 산재하며, 어떻게 고칠 수도 없기에 방치되는 버그도 많다.

이를 해결하기 위해서는 아예 다른 엔진으로 교체하고 코드를 처음부터 다시 짜는 것이 유일한 방법인데, 이는 게임을 새로 하나 개발하는 것이나 다름 없는 수준의 리소스가 들어간다. 서비스 중인 MMORPG에서는 사실상 불가능한 방법이라고 생각되었으나, 2023년 6월 언리얼 엔진으로의 엔진 교체를 발표, "마비노기 이터니티"라는 이름의 프로젝트가 개시되었다.

3.6. 패러독스 인터랙티브에서 제작한 게임들

패러독스 인터랙티브가 직접 만든 게임들(특히 클라우제비츠 엔진)의 코딩이 스파게티인 건 매우 유명하다.

엔진은 어떻게든 개발해서 여러 코어를 사용할 수 있도록 만들고는, 정작 코딩은 코어 하나만을 죽어라 갈궈대는 데다가, 뭐 하나 최적화하려고 하면 다른 부분이 삑사리 나서 짧은 기간동안 순식간에 1.*.0에서 1.*.6까지도 패치가 나오곤 한다. 거기다 렉 먹는 주요 원인을 역설사식 땜빵으로 해결하기도 하니[예시], 업데이트 내겠다고 해놓고서는 뜬금없이 휴가란 휴가는 칼같이 가는 개발진들을 향해 칼을 가는 유저들이 많은 건 당연지사.

이 문제는 개선판 조미니 엔진이 나온 이후에도 어느 정도는 개선되긴 했으나 대부분은 여전한 편이다.

3.7. 토탈 워 시리즈

토탈 워: 엠파이어부터 사용한 워스케이프 엔진은 개량만 거쳐 10년이 넘게 사용한 결과 신작이 나올 때마다 전작의 찌꺼기가 남는다던가 '토탈 워는 1년 묵히고 하는 게임'이라는 말이 있을 정도로 초기 버그가 심각한 시리즈가 되었고, 결국 토탈 워: 워해머 3 발매 이후 개발진이 스스로 코드가 복잡하게 꼬여 있어 버그 수정을 조심스럽게 하고 있다고 실토하는 수준까지 오게 되었다.

3.8. 그 외

Yandere Simulator도 매우 심각한 스파게티 코드로 이루어져 있다고 한다. 그 예시로 NPC의 인공지능 코드에서 매 프레임마다 실행되는 update 함수가 무려 2천 줄이 넘어간다. 개발자인 YandereDev는 개선 의지조차 보이지 않고 있다. 마르쿠스 페르손의 마인크래프트와 비슷한 케이스인데, 마인크래프트도 마찬가지로 마르쿠스 페르손은 개선 의지가 딱히 없었으나 인수를 통해 강제적으로나마 해결이 되었다. 하지만 이쪽은 보다 못한 타사에서 개발자를 파견해서 최적화를 도와주려 시도했으나, 이마저도 YandereDev가 타사 개발자가 처음부터 다시 만든 코드를 알아보지 못해서 이렇게 되면 자신이 더 이상 업데이트를 진행할 수 없다는 이유로 거부하는 등 더 심각한 상황이다.

코딩은 아니지만 4호선 꽈배기굴도 탄생 경위는 완벽하게 스파게티 코드와 똑같다. 여러 가지 혼란 속에 궁여지책으로 나왔는데 25년이 넘게 지난 지금에 와서 고치자니 애물단지가 되어버린 것.

SpaghettiScript라는 난해한 프로그래밍 언어가 존재하는데, 코딩의 형태가 스파게티 면발을 아스키 아트로 늘어놓은 모양새다.

유비소프트 사의 FPS 게임 레인보우 식스 시즈도 상당히 꼬여있는 코드로 인해 각종 버그가 남발한다. 이를 해결하기 위해 무려 한 시즌을 통으로 유지보수에 투자했지만 성과는 거두지 못했다. 특히 게임에 튕기고 재접속에 실패하는 경우 탈주로 판단하고 이용 정지를 먹이기 때문에 유저들의 불만이 매우 크다.

4. 난독화

일부러 스파게티 코드로 만드는 경우. 주로 리버싱을 방해하거나 읽어보는 사람에게 장난치기 위해 만든다.

주로 소스 코드를 컴파일하지 않고 그대로 배포하는 스크립트 언어에서 많이 쓰며, C#이나 Java, ActionScript 같은 관리 언어[10]의 경우 리버싱 툴(디컴파일러)을 사용하면 소스코드를 거의 원본 그대로(!) 뽑을 수도 있기 때문에 난독화 툴이 어느정도 필요하다.

그러나 프로그램 코드 자체는 컴파일러와 인터프리터가 최적화해버리기 때문에 원본 소스코드 그 자체가 유출되지 않는 이상은 복잡하게 바꿔도 별 의미가 없는 경우가 많다. 어차피 리버싱할 최종 결과물은 논리식이 간략화되어 있을 수밖에 없기 때문.

JSFuck은 난독화가 어디까지 갈 수 있는지를 보여주는 예이다.
#define a if(
#define e else
#define E return

// 예시
main()
{
a 3 == 3 ) puts("1234");
e puts("5678");
E 0;
}
}}}

파일:CC-white.svg 이 문서의 내용 중 전체 또는 일부는
문서의 r125
, 번 문단
에서 가져왔습니다. 이전 역사 보러 가기
파일:CC-white.svg 이 문서의 내용 중 전체 또는 일부는 다른 문서에서 가져왔습니다.
[ 펼치기 · 접기 ]
문서의 r125 (이전 역사)
문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)

문서의 r (이전 역사)


[1] 참, 거짓을 저장하는 변수[2] 오죽하면 아래 예제 코드의 움푹 들어간 부분에 파동권합성한 밈이 돌아다닐 정도. 그래서 보통 Promise나 async/await를 사용하여 해결한다.[3] 몇 안되는 GOTO문 적절한 사용 예시가 다중 for 루프 탈출이다.[4] 물론 PHP가 7.x로 올라가서 속도가 어마무시하게 빨라졌기에 불가능한 건 아니다.[5] 구 클라이언트는 어도비 플래시 기반(자세히 말하면 플래시의 파생기술로 런타임으로 실행되는 어도비 에어 기반)으로 만들어서 버그와 보안 취약점이 넘쳐나고 최적화가 개판이었다. 그리고 어도비 플래시가 2020년 지원이 중단되면서 어쨌든 신규 클라이언트의 개발 자체는 필수불가결이었다.[6] 대표적인 발생 사례는 몹이 아래 반 블록 위에 올라가 있을 때.[7] 대표적인 사례가 던파 최고 컨텐츠 중 하나인 '핀드워'에서 발생하는 로딩 버그인데 파티 매칭 중에 특정 유저만 게임 로딩이 안 되어서 해당 던전에 진입 못하고 팅겨져 나가버리는 현상이다. 나머지 파티원으로는 클리어가 불가능하게 돼서 결국 퇴각하는데 이러면 진행에 매우 큰 어려움이 발생한다. 네오플은 근본적인 원인은 해결하지 못하고 결국 로딩 버그 발생시 파티원 전원이 입장 전 상황으로 롤백되게 만들어서 퇴각하는 상황을 막았다.[8] 네오플이 원래 캔디바, 신야구 같은 캐주얼 게임이나 만들던 회사였다.[예시] 스텔라리스의 노예 시장 시스템은 원래 여러 POP들이 시장 위에 올라와야 하지만 현재는 노예 시장 창이 텅 비어있다. 알고보니 역설사가 재깍재깍 사가서 렉을 줄이는 대신 노예 시장 시스템을 유저가 쓸 수 없도록 만들어버리는 황당한 패치를 적용하면서 이렇게 됐다는 것.[10] 실행 파일을 기계어가 아닌 중간 언어(전자는 IL, 후자는 JVM 바이트 코드)로 컴파일하여 배포하는 프로그래밍 언어. 크로스플랫폼과 다양한 기능을 구현하기 위해 컴파일 시 간단한 수준의 최적화만 하기 때문에 후술하는 것처럼 악용되기도 한다.