구조, 성능, 게임(Architecture, Performance, and Games)
무작정 패턴의 바다에 뛰어들기 전에, 소프트웨어 구조와 이를 게임에 적용하는 방법에 관한 저자의 생각부터 살펴보겠습니다. 이 관점을 알아두면 책을 이해하는 데 도움이 됩니다. 적어도 디자인 패턴과 소프트웨어 구조가 얼마나 훌륭한지, 혹은 얼마나 끔찍한지를 놓고 논쟁할 때 꺼낼 논거는 얻을 수 있을 것입니다.
원주 1: 논쟁에서 어느 편에 서는지는 중요하지 않습니다. 저자는 여느 무기상처럼 양쪽 모두에게 무기를 팔 뿐이라고 농담합니다.
1.1 소프트웨어 구조란?
이 책은 3D 그래픽스에 필요한 선형대수나 게임 물리에 필요한 미적분을 다루지 않습니다. AI 탐색 트리에서 알파–베타 가지치기(alpha–beta pruning)를 하는 방법이나, 방 안에서 소리가 울리는 것처럼 반향 효과를 시뮬레이션하는 방법도 설명하지 않습니다.
원주 2: 저자는 벌써부터 책 판매량이 떨어지는 소리가 들리는 것 같다고 덧붙입니다.
대신 그런 요소들 사이에 들어가는 코드를 다룹니다. ‘어떻게 코딩할 것인가’보다는 ‘어떻게 구조를 잡을 것인가’에 관한 책입니다. 모든 프로그램에는 어떤 형태로든 구조가 있습니다. 심지어 모든 코드를 main()에 집어넣고 실행하는 프로그램에도 구조는 있습니다. 따라서 무엇이 좋은 구조를 만드는지, 좋은 구조와 나쁜 구조를 어떻게 구별하는지 살펴보는 편이 더 흥미롭습니다.
저자는 책을 쓰는 5년 내내 이 질문을 고민했습니다. 좋은 구조가 무엇인지 우리는 대체로 감각적으로 압니다. 엉망진창인 코드 때문에 오랫동안 고통받아 본 경험이 있기 때문입니다. 그런 코드에 대해 할 수 있는 최선은, 가능하다면 그 코드를 없애는 것일 때도 있습니다.
원주 3: 물론 우리 자신도 엉터리 코드를 작성해 다른 누군가를 괴롭힌 가해자이기도 합니다.
반대로 아름답게 구성된 코드를 다뤄 본 운 좋은 사람도 있을 것입니다. 그런 코드베이스는 시설이 완벽하고 어디에서나 직원이 기다리고 있는 5성급 호텔처럼 느껴집니다. 이 차이는 어디에서 생길까요?
좋은 소프트웨어 구조란?
저자가 생각하는 좋은 구조는 무언가를 고쳐야 할 때, 마치 그럴 줄 알았다는 듯이 코드가 준비되어 있는 상태입니다. 코드를 거의 건드리지 않고 적당한 함수 몇 개를 호출하는 것만으로 원하는 작업을 할 수 있는 것입니다.
말은 좋지만, 코드를 고쳤는데도 겉으로는 손대지 않은 것처럼 보이게 만들기는 쉽지 않습니다. 하나씩 나눠 생각해 봅시다. 우선 구조는 변경과 관련되어 있습니다. 누군가는 코드를 고쳐야 합니다. 너무 완벽해서든, 너무 지저분해 아무도 보고 싶어 하지 않아서든, 앞으로 전혀 고칠 일이 없다면 구조를 고민할 필요도 없습니다.
설계를 평가하는 척도는 얼마나 쉽게 변경할 수 있는가입니다. 변화가 없는 코드는 출발선을 한 번도 떠나지 않는 달리기 선수와 비슷합니다.
코드를 고치는 방법
기능 추가, 버그 수정, 그 밖의 어떤 이유로 코드를 고치더라도 먼저 기존 코드를 이해해야 합니다. 프로그램 전체를 모두 이해할 필요는 없지만, 수정할 부분과 관련된 코드는 머릿속에 넣어야 합니다.
원주 4: 저자는 이 과정이 광학 문자 인식, 즉 OCR과 얼마나 비슷한지 생각하면 묘한 기분이 든다고 합니다.
이 단계를 대수롭지 않게 여기기 쉽지만, 실제로는 프로그래밍에서 가장 오래 걸리는 부분입니다. 디스크에서 RAM으로 데이터를 불러오는 작업이 느리다고 해도, 눈으로 코드를 읽어 두뇌에 불러오는 것에 비할 바는 아닙니다.
머릿속에 코드의 큰 그림을 그리고 나면 해결책은 비교적 빨리 찾을 수 있습니다. 여러 번 시행착오를 겪을 수는 있지만, 이 과정은 상대적으로 수월합니다. 문제가 무엇이고 어디를 고쳐야 하는지 이해하면 실제 코딩은 별것 아닌 경우도 많습니다.
컴파일에 성공하고 원하는 대로 동작할 때까지 코드를 입력했다고 끝난 것은 아닙니다. 테스트를 작성하고, 코드 리뷰를 요청하기 전에 코드를 정리해야 합니다.
코드 곳곳에 우리가 남긴 지저분한 흔적 때문에 다른 사람이 걸려 넘어지게 하고 싶지는 않을 것입니다. 아주 사소한 변경이 아니라면 새 코드를 기존 코드와 깔끔하게 통합하도록 다듬어야 합니다. 제대로 했다면 동료는 코드가 바뀌었다는 사실조차 눈치채지 못할 수 있습니다.
이 흐름을 그림으로 보면 다음과 같습니다. 업무를 맡고, 코드를 파악하고, 문제를 해결하는 코드를 작성하고, 코드를 정리하는 과정이 반복됩니다.
그림 1-1. 프로그래머의 업무 흐름: 업무 할당 → 코드 파악 → 문제 해결 코드 작성 → 코드 정리
원주 5: ‘테스트’는 잘못 쓴 말이 아닙니다. 일부 게임 코드는 단위 테스트를 작성하기 어려울 수 있지만, 대부분의 게임 코드에는 테스트를 추가할 수 있습니다. 강요하려는 것은 아니지만 아직 단위 테스트를 사용하지 않는다면 자동화된 테스트를 고려해 보세요. 같은 테스트를 매번 손으로 반복하는 것보다 낫습니다.
원주 6: 저자는 이 흐름도가 빠져나갈 길 없는 무한 루프라는 사실이 조금 무섭기도 하다고 덧붙입니다.
디커플링은 어떻게 도움이 될까요?
반드시 그렇지는 않지만 소프트웨어 구조의 많은 부분은 위 그림의 ‘코드 파악’ 단계와 관련됩니다. 코드를 머릿속에 불러오는 일이 느리므로, 한 번에 이해해야 하는 코드의 양을 줄이는 여러 방법이 나왔습니다. 이 책도 5부 전체를 디커플링 패턴에 할애하며, 『GoF의 디자인 패턴』에서도 이를 많이 논의합니다.
커플링은 여러 방법으로 정의할 수 있습니다. 저자는 한쪽 코드가 없으면 다른 쪽 코드를 이해할 수 없을 때 두 코드가 결합되어 있다고 봅니다. 둘을 디커플링하면 각각을 따로 이해할 수 있습니다. 작업이 어느 한쪽에만 관련된다면 그 한쪽 코드만 머릿속에 넣으면 됩니다.
작업 전에 알아야 하는 지식의 양을 줄이는 것, 이것이 저자가 생각하는 소프트웨어 구조의 핵심 목표입니다.
나머지 단계도 중요합니다. 디커플링을 ‘한쪽 코드를 변경해도 다른 코드를 변경하지 않아도 되는 것’으로 정의할 수도 있습니다. 작업을 하려면 어딘가는 수정해야 하지만, 결합이 적은 코드일수록 그 변경이 게임의 나머지 부분에 미치는 영향이 작습니다.
게임에서 생각해 보기 — 점프 높이를 바꾸는 작업
점프 높이를 바꿀 때 입력 처리, 물리 계산, 애니메이션, UI 코드를 모두 읽고 수정해야 한다면 ‘코드 파악’ 비용부터 커집니다. 점프 규칙이 한곳에 모이고 다른 시스템이 그 결과만 받는다면, 같은 변경을 이해하는 데 필요한 범위가 작아집니다. 클래스를 많이 만드는 것 자체가 목표는 아닙니다. 실제로 함께 바뀌는 책임과 의존 관계를 확인하는 것이 먼저입니다.
1.2 좋은 구조에는 어떤 비용이 들까요?
앞의 설명만 보면 모든 것을 디커플링하면 될 것 같습니다. 수정 사항이 생겨도 메서드 한두 개만 손보고 나머지 코드 위를 날아다니듯 개발할 수 있을 것입니다.
그래서 사람들은 추상화, 모듈화, 디자인 패턴, 소프트웨어 구조에 열광합니다. 잘 구조화된 코드에서 일하는 것은 즐겁고, 누구나 높은 생산성을 원합니다. 좋은 구조는 생산성을 크게 높입니다. 그 차이가 얼마나 큰지는 아무리 강조해도 지나치지 않습니다.
하지만 공짜는 아닙니다. 좋은 구조를 만들고 유지하려면 많은 노력과 원칙이 필요합니다. 기능 하나를 추가하거나 코드를 조금 변경할 때마다 나머지 코드와 깔끔하게 통합하도록 노력해야 합니다. 개발 기간 동안 수천 번 수정하면서도 구조를 유지하려면 상당한 수고가 듭니다.
원주 7: 구조 유지에는 지속적인 관심이 필요합니다. 아름다운 구조로 시작한 프로그램도 꼼수(hack)를 반복해서 추가하다 보면 망가질 수 있습니다. 정원에 식물을 심는 것으로 끝나지 않고 잡초를 뽑고 가지치기를 해야 하는 것과 같습니다.
이제 어디를 디커플링하고 추상화할지 고민해야 합니다. 나중에 쉽게 바꾸려면 어느 부분을 확장 가능하게 만들지도 결정해야 합니다.
사람들은 이 부분을 재미있어합니다. 미래의 개발자나 미래의 자신에게 제약이 적고, 강력하고, 확장하기 쉬운 코드베이스를 주고 싶어 합니다. 이른바 ‘강력한 만능 게임 엔진’을 만들고 싶은 것입니다.
바로 이 지점이 어려워지는 부분이기도 합니다. 추상 계층이나 확장 지점을 추가하는 것은 그 유연성이 나중에 필요하리라고 예측했기 때문입니다. 이를 위해 코드를 더하고 복잡도를 높이면 개발·디버깅·유지보수에 시간이 더 듭니다.
예측이 맞아 실제로 그 코드를 수정하게 된다면 투자한 노력을 돌려받습니다. 하지만 미래를 예측하기는 어렵습니다. 만들어 둔 모듈을 사용하지 않는다면 다뤄야 할 코드만 늘어난 셈이므로 아예 만들지 않는 것보다 못할 수 있습니다.
원주 8: 어떤 개발자는 YAGNI, 즉 “You Aren’t Gonna Need It(나중에 필요하지 않을 것이다)”을 되새기며 미래에 필요할 기능을 미리 예측하고 구현하려는 유혹을 피합니다.
확장성에 지나치게 빠지면 인터페이스, 추상화, 플러그인 시스템, 추상 클래스, 가상 메서드, 확장 지점이 덕지덕지 붙습니다. 그러면 실제 작업을 하는 코드를 찾는 데 오히려 더 오래 걸립니다. 변경에 도움이 될 인터페이스가 어딘가에 있어도 찾기가 어렵습니다.
디커플링은 원래 수정 전에 이해해야 하는 양을 줄여야 합니다. 그런데 추상 계층 자체가 머릿속 용량을 채워 버리는 상황이 생깁니다. 이런 코드 때문에 소프트웨어 구조, 특히 디자인 패턴에 등을 돌린 사람도 많습니다.
구조 자체에 둘러싸이다 보면 게임을 출시해야 한다는 목적을 잊기 쉽습니다. 엔진은 게임을 만들기 위한 것인데, 확장성이라는 세이렌의 노래에 홀려 엔진 자체에 시간을 낭비하는 개발자가 얼마나 많았을까요?
원주 9·옮긴이: 세이렌은 그리스 신화에서 아름다운 노래로 선원들을 홀려 바다에 뛰어들게 만드는 생물입니다.
1.3 성능과 개발 속도
소프트웨어 구조와 추상화가 게임의 실행 성능을 떨어뜨린다는 비판도 있습니다. 코드를 유연하게 만드는 패턴은 가상 함수, 인터페이스, 포인터, 메시지 같은 장치에 의존하는 경우가 많고, 이들은 어느 정도 실행 시간 비용을 요구합니다.
그럴 만한 이유가 있습니다. 소프트웨어 구조의 상당 부분은 코드를 유연하고 쉽게 변경할 수 있게 하려고 존재합니다. 이는 프로그램이 전제하는 가정을 줄인다는 뜻입니다.
인터페이스를 쓰면 지금의 클래스뿐 아니라 같은 인터페이스를 구현한 어떤 클래스가 들어와도 동작하게 할 수 있습니다. 관찰자 패턴(4장)이나 이벤트 큐(15장)를 쓰는 이유도 당장은 둘만 통신하더라도 나중에 셋, 넷으로 쉽게 늘리고 싶기 때문입니다.
반면 성능 최적화는 구체적인 가정과 제한을 활용합니다. 적이 256개 이하라고 확신하면 식별자를 1바이트로 압축할 수 있습니다. 특정 자료형의 특정 메서드만 호출한다고 확신하면 가상 함수 대신 정적으로 바인딩된 함수를 직접 호출하거나 인라인으로 만들 수 있습니다. 모든 개체가 같은 클래스라면 배열에 연속해서 담을 수 있습니다. 이 마지막 주제는 17장 데이터 지역성에서 다룹니다.
원주 10: C++ 템플릿은 흥미로운 반례입니다. 템플릿 메타프로그래밍은 실행 시간의 추가 부담 없이 인터페이스를 추상화하는 방법을 제공합니다. 특정 클래스의 메서드를 직접 호출하면 코드 작성 시점에 클래스를 고정합니다. 가상 메서드나 인터페이스를 통하면 실행 시점에 호출할 클래스가 정해져 유연하지만 약간의 실행 비용이 생깁니다. 템플릿은 컴파일 때 인스턴스화되면서 클래스를 결정하므로 이 둘 사이의 절충으로 볼 수 있습니다.
그렇다고 유연성이 나쁘다는 뜻은 아닙니다. 유연해야 게임을 쉽게 바꿀 수 있고, 개발 속도는 게임의 재미를 찾는 데 꼭 필요합니다. 윌 라이트 같은 사람도 기획서만으로 게임 밸런스를 완성할 수는 없습니다. 반복 개발과 실험이 함께 필요합니다.
아이디어를 빨리 시험하고 느낌을 빨리 확인할수록 더 많이 시도할 수 있고, 멋진 결과를 찾을 가능성도 높아집니다. 딱 맞는 게임 메커니즘을 찾은 뒤에도 튜닝에는 시간이 많이 듭니다. 밸런스가 조금만 무너져도 게임은 망가질 수 있습니다.
쉬운 답은 없습니다. 빠른 프로토타이핑을 위해 유연하게 만들면 성능 비용이 생기고, 최적화하면 유연성이 줄어듭니다. 저자의 경험으로는 재미있는 게임을 최적화하는 편이 최적화된 게임을 재미있게 만드는 것보다 쉽습니다. 처음에는 유연하게 유지하고, 기획이 확실해진 뒤 추상 계층을 줄여 성능을 높이는 절충도 가능합니다.
두 가지 속도를 구분해 보세요.
‘새로운 적 행동을 반나절 안에 실험할 수 있다’는 개발 속도이고, ‘한 프레임의 AI 계산이 0.5ms 안에 끝난다’는 실행 속도입니다. 두 수치는 서로 다른 목표입니다. 변경하기 쉬운 구조로 실험한 뒤, 실제로 느린 부분을 측정하고 그 부분에 필요한 가정을 좁혀 가면 절충의 근거가 생깁니다.
1.4 나쁜 코드에도 장점이 있습니다
상황마다 알맞은 코딩 방식이 있습니다. 이 책은 주로 코드를 깔끔하고 유지하기 쉽게 만드는 방법을 다룹니다. 저자도 제대로 만드는 편을 좋아하지만, 엉성한 코드 역시 나름의 가치가 있다고 말합니다.
잘 구조화된 코드를 작성하려면 생각할 시간이 필요합니다. 좋은 구조를 개발 내내 유지하려면 더욱 많은 노력이 듭니다. 야영장을 처음 왔을 때보다 깨끗하게 해 놓고 떠나는 야영객처럼 코드를 다뤄야 합니다.
원주 11·옮긴이: 로버트 마틴이 『Clean Code』에서 소개한 ‘보이스카우트 규칙’과 같은 개념입니다.
같은 코드로 오랫동안 작업해야 한다면 이런 방식이 좋습니다. 하지만 게임을 기획하는 과정에는 많은 실험과 탐구가 필요합니다. 특히 초반에는 나중에 버릴 코드를 자주 작성합니다.
어떤 기획을 확인하기 위한 임시 코드의 구조까지 멋지게 만들려고 하면, 실제 화면에 띄워 피드백을 받기까지 시간이 더 걸립니다. 기획이 별로라고 밝혀지면 코드를 우아하게 만드는 데 들인 시간도 코드와 함께 버려집니다.
원주 12·옮긴이: 공을 들인 코드는 버리기도 더 어렵습니다.
기획을 확인할 기능만 간신히 돌아가도록 빠르게 작성하는 프로토타이핑은 적절한 프로그래밍 실천입니다. 다만 한 가지 조건이 있습니다. 버릴 코드는 나중에 실제로 버릴 수 있어야 합니다. 저자는 다음과 같은 상황을 여러 번 보았다고 합니다.
팀장: “이번 기획을 시험해 보고 싶어. 프로토타입이니까 대충 빠르게 만들면 얼마나 걸릴까?”
팀원: “테스트나 문서가 없고 버그가 많아도 된다면, 며칠 안에 임시 코드를 만들 수 있을 것 같습니다.”
팀장: “좋아. 그렇게 진행해 줘.”
며칠 뒤…
팀장: “프로토타입이 괜찮네. 몇 시간만 더 정리하면 출시용 구현도 끝낼 수 있겠지?”
임시 코드를 사용하는 사람에게는 겉으로 동작해 보여도 장기적으로 유지할 상태는 아니며, 나중에 반드시 다시 만들어야 한다는 점을 분명하게 알려야 합니다. ‘버릴 코드’를 계속 유지해야 할 가능성이 있다면 처음부터 방어적으로 잘 만들어야 할 수도 있습니다.
원주 13: 프로토타입이 그대로 출시 코드가 되는 것을 막는 한 방법은 아예 다른 프로그래밍 언어로 작성하는 것입니다. 그러면 출시 전에 반드시 다시 작성하게 만들 수 있습니다.
1.5 세 가지 목표의 균형 잡기
우리에게는 다음 세 목표가 있습니다.
1.
프로젝트 개발 기간 내내 이해하기 쉬운 깔끔한 구조를 유지하고 싶습니다.
2.
게임의 실행 성능을 최적화하고 싶습니다.
3.
지금 개발하는 기능을 최대한 빨리 구현하고 싶습니다.
원주 14: 셋 모두 일종의 ‘속도’에 관한 목표입니다. 차례로 장기적인 개발 속도, 게임 실행 속도, 단기적인 개발 속도입니다.
이 목표들은 어느 정도 서로 충돌합니다. 좋은 구조는 장기 생산성을 높이지만, 좋은 상태를 유지하려면 코드를 바꿀 때마다 노력을 더 들여야 합니다.
가장 빨리 구현한 결과가 실행도 가장 빠른 경우는 드뭅니다. 최적화에는 개발 시간이 많이 들며, 최적화한 코드는 고착되기 쉽습니다. 극도로 최적화된 코드는 유연하지 않고 고치기 어렵습니다.
‘고민은 나중에 하고 지금 일을 먼저 끝내라’는 압박은 늘 있습니다. 그렇지만 기능을 계속 급하게 밀어 넣으면 꼼수와 버그가 늘고 일관성이 사라져 생산성이 점점 떨어집니다.
세 목표 모두 장단점이 있으므로 명쾌한 정답보다는 절충이 필요합니다. 저자는 이 설명 때문에 좌절했다는 독자의 이메일도 받았습니다. 그저 게임을 만들고 싶은 지망생에게 “정답은 없고 오답에 대한 다양한 취향만 있다”는 말은 위협적으로 들릴 수 있습니다.
하지만 저자에게는 이런 어려움이 오히려 신나는 일입니다. 평생을 바쳐야 달인이 될 수 있는 분야에는 서로 얽힌 제약이 있습니다. 쉬운 방법이 있다면 모두 그 방법만 쓸 것입니다. 일주일 만에 완전히 익힐 수 있다면 금방 지겨워질 것입니다. ‘삽질’만으로 굉장한 경력을 쌓았다는 이야기는 들어본 적이 없다고 농담합니다.
원주 15: 다만 삽질을 따로 조사한 것은 아니므로 틀렸을 수도 있습니다. 삽질 애호가, 협회, 서브컬처가 있을지도 누가 알겠느냐는 농담입니다.
게임도 비슷합니다. 체스처럼 말들 사이의 균형이 정교한 게임은 평생을 바쳐도 모든 전략을 탐구할 수 없습니다. 반대로 필승 전략 하나만 반복하면 이기는 게임은 금방 지겨워져 그만두게 됩니다.
1.6 단순함(Simplicity)
이 제약을 조금이라도 완화할 방법이 있다면 단순함일 것입니다. 저자는 최대한 간결하고 문제를 직접 해결하는 코드를 작성하려고 노력합니다. 읽으면 의도가 곧바로 드러나고, 다른 해결 방식은 떠오르지 않을 정도의 코드입니다.
먼저 자료구조를, 다음으로 알고리즘을 잡고 거기서부터 다른 방법을 찾습니다. 코드를 단순하게 유지하면 전체 양이 줄고, 고치기 전에 머릿속에 담아야 할 양도 줄어듭니다.
부가 비용과 실행할 코드가 적어 실행 속도까지 빠른 경우도 많습니다. 물론 코드가 짧더라도 그 안에 반복이나 재귀 호출이 많다면 이야기는 다릅니다.
단순한 코드를 작성하는 데 시간이 적게 든다는 뜻은 아닙니다. 코드 양이 적으니 빨리 쓸 수 있을 것 같지만, 좋은 해결책은 코드를 계속 붙이는 대신 필요 없는 부분을 최대한 덜어 내는 데서 나옵니다.
원주 16: 파스칼은 시간이 없어 편지를 짧게 쓰지 못했다고 썼고, 생텍쥐페리는 보탤 것이 없을 때가 아니라 덜어 낼 것이 없을 때 완벽해진다고 말했습니다. 저자 역시 책을 퇴고할 때마다 글이 줄었고, 어떤 장은 20% 이상 줄었다고 합니다.
현실의 문제는 대부분 우아하지 않습니다. 수많은 사용 사례가 얽혀 있습니다. A일 때는 B를 해야 하고, C일 때는 D를 해야 하는 식입니다.
가장 직접적으로 떠오르는 해결법은 모든 경우를 하나씩 코드에 옮기는 것입니다. 생각나는 대로 조건문을 늘어놓는 방식이며, 초보 개발자가 흔히 이렇게 합니다.
하지만 이런 코드는 우아하지 않고, 예상에서 조금만 벗어난 입력에도 동작하지 않을 수 있습니다. 우아한 해결책은 적은 로직으로 많은 사용 사례를 정확하게 처리하는 일반적인 코드입니다.
이를 찾는 과정은 패턴 찾기나 퍼즐 풀기와 닮았습니다. 흩어진 사용 사례에서 숨은 규칙을 찾기란 쉽지 않지만, 발견하면 큰 성취감을 느낄 수 있습니다.
직접 점검해 보기
최근 기능 하나를 떠올려 보세요. 수정 전에 읽은 파일은 몇 개였고, 실제로 수정한 파일은 몇 개였나요? 자주 함께 바뀌는 코드를 찾은 다음, 당장 필요하지 않은 확장 장치를 추가하지 않고도 책임을 모을 수 있는지 살펴보세요. 단순함은 글자 수를 무조건 줄이는 것이 아니라, 문제를 설명하는 규칙을 적게 만드는 데 가깝습니다.
1.7 마치며
서문을 건너뛰는 사람도 많은데 여기까지 읽었다면 대단합니다. 저자는 독자의 인내에 보답하며 다음 조언을 남깁니다.
•
추상화와 디커플링을 잘 활용하면 코드를 점차 쉽고 빠르게 작성할 수 있습니다. 다만 지금 다루는 코드에 유연성이 필요하다는 확신이 없다면, 적용하느라 시간을 낭비하지 마세요.
•
개발 내내 성능을 생각하고 최적화하기 좋은 형태로 설계하세요. 하지만 가정을 코드에 깊이 새기는 저수준의 핵심 최적화는 가능하면 늦추세요.
•
기획을 확인할 수 있도록 빠르게 개발하되, 서두르느라 코드를 엉망으로 만들지는 마세요. 결국 그 코드로 계속 작업할 사람은 우리입니다.
•
나중에 버릴 코드를 잘 만들겠다고 시간을 낭비하지 마세요. 록 스타가 호텔 방을 어지르는 것은 다음 날 계산하고 떠나면 된다는 사실을 알기 때문이라는 비유입니다.
•
무엇보다 재미있는 것을 만들고 싶다면, 만드는 과정부터 재미있게 느껴 보세요.
원주 17: 최적화를 늦추라는 조언은 출시 두 달 전까지 ‘1FPS밖에 나오지 않는 문제’를 미루라는 뜻이 아닙니다.





