1. 개요
프로그래밍에서 기술적 부채에 해당하는 분류로, 컴퓨터 프로그램의 흐름이 복잡하게 뒤엉킨 모습을 스파게티가 엉킨 모습에 비유한 표현이다.스파게티 코드는 겉으로는 비교적 제대로 작동하는 것처럼 보이나, 가독성이 많이 떨어지고 유지보수가 매우 어려워진다. 작동 방식을 변경하거나 버그를 찾거나 개선하는 등 코드를 수정하는 모든 작업에 방해가 된다. 특히 OOP에서 상속(프로그래밍) 계층이 너무 많아 생긴 스파게티 코드는 따로 라자냐 코드라고도 한다. # 물론 OOP에도 고전적인 스파게티 코드가 생길 수 있다.
스파게티 코드는 프로그래밍 초보자들이 많이 작성한다. 심각한 경우 '수정할 바에야 다시 만드는 게 낫다'는 농담을 하기도 한다. 실제로 규모가 지나치게 커진 스파게티 코드는 처음부터 다시 만드는 것이 유일한 해결책이다. PC게임을 모바일로 포팅할 경우에는 스파게티 코드가 아니더라도 처음부터 다시 만드는 것이 경제적일 때가 많다. 아래의 실제 사례 문단 참고.
2. 발생 원인
보통 계획 없이 바로 코딩을 시작하거나 요구사항이 원래 기획한 범위를 크게 벗어날 때, 혹은 프로그래머의 알고리즘 구현 실력이 지나치게 떨어질 때 주로 나타난다. 중간에 인원이 바뀌면서 전임자가 짜 놓은 코드의 인수인계가 잘 되지 않고 이해도가 떨어질 경우 발행하기 쉽다. 발적화도 덤으로 나타나는 경우가 많지만, 스파게티 소스 코드라고 모두 실제 작동에서 비효율적인 것은 아니다.-
함수/메소드가 둘 이상의 작업을 한다.
하나의 함수/메소드에서는 논리적으로 구분되는 가장 작은 단위의 작업을 처리함이 이상적이다. 이 규칙을 어겼을 때 로직이 꼬이면서 스파게티 코드가 발생한다. 하나의 함수/메소드의 코드의 길이가 1000줄이 넘는다면 거의 대부분 이런 경우다. 또 다른 기준으로, 로컬 변수의 특정한 그룹이 함수의 일부 영역에서만 변경되고 나머지 영역에서는 단지 읽기만 할 때 그 변수를 마지막으로 조작한 지점까지를 하나의 작업 단위로 정의하고 코드를 분리할 수도 있다. 이러한 면에서 혹자는 함수의 매개변수에 boolean[1]를 받으면 안 된다고 한다.
- 논리적으로 하나의 작업 단위를 억지로 둘 이상의 함수/메소드로 쪼개는 경우
-
여러 함수/메소드들이 지나치게 서로 의존하면서 얽힌 것.
특히 전혀 상관없는 클래스들의 메소드 (인터페이스도 아니고 상속한 것도 아닌...)들이 뒤죽박죽 얽혔다면 답이 없다. 이럴 경우 메소드 하나를 조금 손 보면 프로그램 전체가 오작동하기 십상이기 때문에 디버깅은 물론이고 기능 확장 등을 하기가 악몽과 같다. 귀찮아서 다시 짜지 않고 계속 방치할 경우 언제 터질지 모르는 시한 폭탄이 된다. 거대한 메소드의 가독성을 높이기 위해서 특정 부분들을 작은 메소드들로 쪼개서 코딩하는 건 좋지만 너무 엉키지 않게 조심해야 한다.
-
if-else 문의 계층이 지나치게 얽혔다.
여러 조건을 동시에 비교하는 경우에는 진리표나 패턴 매칭을 대신 사용해야 한다. 순서도를 그렸을 때 마름모(조건분기 기호)가 나무뿌리처럼 얽힌 코드는 훌륭한 스파게티 코드다. 개별 변수 a, b, c 를 if-else로 하나씩 비교하는 대신 if a and (not b) and c then 'do something' 하는 식으로 모든 조건을 한꺼번에 검사하면서 내려가라는 얘기다. 혹은 별도의 '평가 함수'를 작성하는 방법도 있다. if(eval_function(a,b,c, goal) == true) then 'do someting' 하는 식이다.
C++에서 진리표를 구현하는 방식은 여러 가지인데 그 중에서 비트 마스크 방식의 진리표는 다음과 같다. int 사용시 32개의 조건을, long 사용시 최대 64개의 TRUE/FALSE 조건을 고속으로 검사할 수 있다.
{{{#!syntax cpp
#include <stdio.h>
int main(void) {
bool a = true;
bool b = false;
bool c = 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");
}
}}}
-
콜백 함수
콜백 계층이 지나치게 들어가 코드가 대각선으로 쓰여지는 경우 문제가 된다. 보통 3~5단계를 초과하는 콜백 체인은 스파게티 코드로 간주할 수 있다. 콜백 많기로 둘째가라면 서러울 Node.js가 가장 대표적인 예시. 아예 node.js의 단점을 콜백 지옥이라고 표현할 정도.[2] 콜백 함수 문서 참조.
{{{#!syntax javascript
if (err) {
});
console.log('Error finding files: ' + err)
} else {
files.forEach(function(filename, fileIndex) {
}
console.log(filename)
gm(source + filename).size(function(err, values) {
})
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) {
aspect = (values.width / values.height)
widths.forEach(function(width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
}
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))
if (err) console.log('Error writing file: ' + err)
})
}}}
-
다중 for 루프
행렬을 처리할 때에는 다중 for루프가 하나의 논리적 처리 단위이므로 다중 for루프 자체는 스파게티 코드가 아니다. 문제는 트리 자료구조 등을 순회할 때 다중 for루프를 통해 처리하는 경우다. 트리 자료구조(대표적으로 XML/ JSON)를 처리할 때에는 과감히 재귀함수를 사용해 줄 필요가 있다. 특히 다중 for 루프에서 break와 continue를 마구 남발하면 GOTO문을 사용한 것보다 코드의 흐름을 추적하기 더욱 더 어려워진다[3]. 재귀함수가 스택 깊이를 뚫을 수 있다거나 함수 호출 오버헤드가 있어서 쓸 수 없는 경우가 있어서 다중 for문을 사용해야 하는 경우도 있으나 이런 경우는 흔한 경우는 아니다.
-
포인터 남용
포인터는 양날의 검과 같다. 분명 강력하고 유용한 도구이지만 잘못 사용하면 그야말로 재앙이 된다. 값 복사의 오버헤드가 도저히 용납이 안 될 정도로 성능하락을 보이는 게 아니면 포인터 전달보다는 값 복사를 사용하는 편이 좋고, 그게 어려울 때에도 포인터를 '상수'로 간주해 한 번 할당한 값을 변경하지 않는 게 좋고, 그것도 여의치 않으면 최소한 포인터를 조작하는 로직을 코드의 한 군데에 모두 몰아넣어야 한다.
-
GOTO 남용
항목 참조.
-
잘못된
프로그램 최적화 방법 남용
쓸데없는 루프 언롤링, 과도한 인트린식 사용, 매크로 남용 등이다. 컴파일러의 최적화 엔진을 불신하는 꼰대 프로그래머들의 경향이기도 한데 이들이 만든 스파게티 코드는 컴파일러조차 해석에 애를 먹어서 최적화 엔진이 최적화를 포기하기 때문에 저성능, 불안정, 낮은 보안성을 모두 갖춘 쓰레기가 만들어진다.
-
용도에 맞지 않는
프로그래밍 언어의 사용
그래픽 프로세싱용 언어로 데이터베이스 로직을 구현하거나 논리 프로그래밍 언어로 기계제어를 시도하는 경우 등. 원래 프로그래밍 언어가 상정한 활용 영역을 벗어나서 코딩하려니 라이브러리 지원도, 문법 지원도 받지 못하고 차라리 어셈블리어가 읽기 쉬워질 지경의 코드를 만들게 된다. C++이나 Python 같은 건 범용 언어니 그렇다 치더라도, PHP로 인공지능 로직을 구현하려고 시도하는 건 도대체...[4]
-
코딩 스타일이 통일되지 못함
특히 다소 마이너한 주제의 무보수 오픈소스 프로젝트에서 흔히 일어날 수 있는 일이다. 주제의 매니악함과 보수가 없음으로 인해 유입이 적고 엄격한 기여 규칙을 적용하기 어려워지기 때문이다. 또한 개발기간이 오래된 경우 과거의 잔재들이 남아있을 수 있는데, 이 경우 과거의 잔재가 버그의 온상이 된다. 특히 어느 정도 C언어 스타일로 코딩할 수 있는 C++이 이런 경우가 많다.
- 매크로 남용
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은 난독화가 어디까지 갈 수 있는지를 보여주는 예이다.
-
매크로를 이용해 흔한 표현도 뒤바꾼다
{{{#!syntax cpp
#define e else
#define E return
// 예시
main()
{
a 3 == 3 ) puts("1234");
e puts("5678");
E 0;
}e puts("5678");
E 0;
}}}
- .net obfuscator 같은 난독화 툴을 쓴다. 이런 프로그램은 변수, 상수, 함수의 이름을 컴퓨터만 알아볼 수 있게 마구 섞어버리고, 보기에만 더 복잡한 같은 일을 하는 다른 논리식으로 바꿔친다. RTTI(RunTime Type Information) 같은 기술 구현의 문제 때문에 원래 실행시간에는 필요 없는 자료구조나 변수 크기 같은 정보도 프로그램에 포함되는데, 이 부분을 중점적으로 복잡하게 꼬아 사람이 읽기 힘들게 한다. 프로그램에 따라서는 소스코드의 순서나 include 관계도 등도 손보는 경우가 있다.
[1]
참, 거짓을 저장하는 변수
[2]
오죽하면 아래 예제 코드의 움푹 들어간 부분에
파동권을
합성한 밈이 돌아다닐 정도. 그래서 보통 Promise나 async/await를 사용하여 해결한다.
[3]
몇 안되는 GOTO문 적절한 사용 예시가 다중 for 루프 탈출이다.
[4]
물론 PHP가 7.x로 올라가서 속도가 어마무시하게 빨라졌기에
불가능한 건 아니다.
[5]
구 클라이언트는
어도비 플래시 기반(자세히 말하면 플래시의 파생기술로 런타임으로 실행되는 어도비 에어 기반)으로 만들어서 버그와 보안 취약점이 넘쳐나고 최적화가 개판이었다. 그리고 어도비 플래시가 2020년 지원이 중단되면서 어쨌든 신규 클라이언트의 개발 자체는 필수불가결이었다.
[6]
대표적인 발생 사례는 몹이 아래 반 블록 위에 올라가 있을 때.
[7]
대표적인 사례가 던파 최고 컨텐츠 중 하나인 '
핀드워'에서 발생하는 로딩 버그인데 파티 매칭 중에 특정 유저만 게임 로딩이 안 되어서 해당 던전에 진입 못하고 팅겨져 나가버리는 현상이다. 나머지 파티원으로는 클리어가 불가능하게 돼서 결국 퇴각하는데 이러면 진행에 매우 큰 어려움이 발생한다. 네오플은 근본적인 원인은 해결하지 못하고 결국 로딩 버그 발생시 파티원 전원이 입장 전 상황으로 롤백되게 만들어서 퇴각하는 상황을 막았다.
[8]
네오플이 원래 캔디바, 신야구 같은 캐주얼 게임이나 만들던 회사였다.
[예시]
스텔라리스의 노예 시장 시스템은 원래 여러 POP들이 시장 위에 올라와야 하지만 현재는 노예 시장 창이 텅 비어있다. 알고보니 역설사가 재깍재깍 사가서 렉을 줄이는 대신 노예 시장 시스템을 유저가 쓸 수 없도록 만들어버리는 황당한 패치를 적용하면서 이렇게 됐다는 것.
[10]
실행 파일을 기계어가 아닌 중간 언어(전자는 IL, 후자는 JVM 바이트 코드)로 컴파일하여 배포하는 프로그래밍 언어. 크로스플랫폼과 다양한 기능을 구현하기 위해 컴파일 시 간단한 수준의 최적화만 하기 때문에 후술하는 것처럼 악용되기도 한다.