쾌락코딩

python3으로 알고리즘 풀 때 팁 정리

|

정렬을 유용하게 사용하자

백준 그룹 단어 체커 1316문제를 풀다가 아주 짧은 코드로 푼 사람이 있어 살펴보았더니 정렬을 사용하는 신박한 방법을 발견했다. 우선 나의 풀이방법은 이미 나왔던 단어들을 history라는 변수에 배열로 저장하고, 입력 받은 변수의 character가 history에 존재한다면 그룹 단어가 아닌 것으로 간주하는 방법을 사용했다. 그러나 정렬을 사용하면 아주 쉽게 풀이가 가능했다. 핵심은 key를 조작하는 것이었다.

res = 0
for i in range(int(input())):
    s = input()
    if list(s) == sorted(s, key=s.find):
        res += 1
print(res)

s가 ‘wordo’라는 문자열 이라고 했을 때 list(s) 의 결과 값은

['w','o','r','d','o']

이다.

그리고 일반 적으로 sorted(s)를 하면 결과 값은 사전 순으로 정렬 되기 때문에

['d','o','o,','r','w']

이다. 여기서 sorted함수에 key값으로 익명 함수 lamda를 적용하면 lamda함수의 리턴 값에 맞게 정렬 순서를 조작 할 수 있다. 위의 예에서는 ‘wordo’ 스트링의 find함수를 lamda함수에 넘겨주었다. find 함수는 string을 인자로 받는데 sorted에서 lamda로 넘겨준 함수에 정렬 될 배열의 요소 하나하나가 인자로 넘어간다. 먼저 처음 문자인 w가 넘어가면 find 함수는 첫 w의 위치인 0 을 반환한다. 따라서 w의 위치는 맨 처음 0 이 될 것이다. 두 번째로 ‘o’가 인자로 넘어가면 ‘wordo’에서 첫 ‘o’의 위치인 1이 반환 되므로 두 번째 ‘o’의 위치는 1이다. 이렇게 ‘r’과 ‘d’마찬가지로 적용 되는데, 마지막에 ‘o’가 다시 한번 나타난다. 이 ‘o’가 ‘word’.find(‘o’)와 같이 인수로 넘어간다면 처음 o의 위치인 1을 반환하게 되어 1의 위치에 있는 ‘o’를 밀어내고 그자리에 앉게 된다. 결국 wordo와 배열이 달라지므로 앞에 있던 단어를 반복 한 것이 됨으로 결과는 그룹 단어가 아닌 것이다.

정확히 기억은 잘 나진 안지만 가끔씩 배열을 정렬 하고 생각해보면 쉽게 풀렸던 문제들이 몇몇 있었다. 시간이 오래 걸리는 것도 아니니 정렬을 한 다면 풀이가 어떻게 될지 생각해보는 것도 나쁘지 않은 것 같다.

combinations

백준 2309번 (일곱 난쟁이)는 9명의 난쟁이 후보중에서 2명의 가짜 난쟁이를 구분해 실제 7명의 난쟁이를 찾아내는 문제다. 일반적으로 2중 for문을 순회하면서 모든 경우의 수를 확인해 보는 문제인데, python이 가진 combinations 함수를 사용하면 굳이 일일히 2중 for문을 쓸 필요가 없이 간단하게 해결이 가능했다. 간단한 사용법은 아래와 같다.

from itertools import combinations

candidate = [1,2,3,4,5,6,7,8,9]
combi = combinations(candidate,7)
for i in combi:
    print(sum(i))
    # bla bla

brute force문제를 풀 때 유용하게 사용할 수 있을 것 같다.

Cannot add foreign key constraint

|

어떤 에러인가?

Node.js와 sequelize로 웹 서버, 데이터베이스를 구축했다. 실제 프로덕션 환경에서 사용할 데이터베이스는 aws RDS인데 개발중에 사용하면 괜히 돈이 나갈까봐 local환경의 데이터베이스에서 작업해왔다. local에서는 전혀 문제없이 원하는 모든 테이블이 정상적으로 생성되었는데, aws RDS에서 실행해보니 아래와 같은 에러가 떳다.

CREATE TABLE IF NOT EXISTS `folders`
    (`id` CHAR(36) BINARY , `folderName` VARCHAR(255),
     `folderCoverImage` VARCHAR(255), `createdAt` DATETIME NOT NULL,
     `updatedAt` DATETIME NOT NULL, `fk_user_id` CHAR(36) BINARY,
     `fk_category_id` CHAR(36) BINARY,
    PRIMARY KEY (`id`),
    FOREIGN KEY (`fk_user_id`) REFERENCES `users` (`id`)
        ON DELETE CASCADE ON UPDATE RESTRICT,
    FOREIGN KEY (`fk_category_id`) REFERENCES `categories` (`id`)
        ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB;

Unhandled rejection SequelizeDatabaseError: Cannot add foreign key constraint

당황스럽기도 했고 신기하기도 했다. 분명히 동일한 코드에 동일한 DB(MYSQL)스펙인데.. 그저 host의 주소만 바뀐것 같은데 어떻게 이런일이 발생한 걸까?

상황

sequelize가 처리해주는 테이블 생성 statement는 아래와 같았다.

Executing (default): CREATE TABLE IF NOT EXISTS `users` (`id` CHAR(36) BINARY , `username` VARCHAR(255), `email` VARCHAR(255) UNIQUE, `socialProvider` VARCHAR(255), `profileImg` TEXT, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Executing (default): CREATE TABLE IF NOT EXISTS `posts` (`id` CHAR(36) BINARY , `postTitle` VARCHAR(255), `fk_user_id` CHAR(36) BINARY, `subTitle` TEXT, `editorState` TEXT, `bookCoverImg` VARCHAR(255), `like` INTEGER DEFAULT 0, `starRating` INTEGER DEFAULT 0, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (`fk_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Executing (default): CREATE TABLE IF NOT EXISTS `categories` (`id` CHAR(36) BINARY , `name` VARCHAR(255) UNIQUE, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Executing (default): CREATE TABLE IF NOT EXISTS `tags` (`id` CHAR(36) BINARY , `name` VARCHAR(255) UNIQUE, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Executing (default): CREATE TABLE IF NOT EXISTS `folders` (`id` CHAR(36) BINARY , `folderName` VARCHAR(255), `folderCoverImage` VARCHAR(255), `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `fk_user_id` CHAR(36) BINARY, `fk_category_id` CHAR(36) BINARY, PRIMARY KEY (`id`), FOREIGN KEY (`fk_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, FOREIGN KEY (`fk_category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB;
Unhandled rejection SequelizeDatabaseError: Cannot add foreign key constraint

구글링을 꽤 오랫동안 해보았는데, 대부분의 원인은 테이블 생성 순서인 경우가 많았다. 예를 들자면, folders테이블은 users와 categories테이블을 참조해야하는데, 만약 folders 테이블이 먼저 생성된다면 folders의 foreign key를 설정하는 부분에서 user테이블 또는 categories테이블을 찾을 수 없어서 이런 에러가 나는 것이다.

그러나 아쉽게도 나의 경우는 아니었다. 순서는 정확했다. 코드를 봐도 알 수 있고 local환경에서는 잘 돌아가는 것을 봐도 알 수 있다.

원인

결국 성공할 때와 실패할 때의 차이점은 database환경일 뿐이라는 결론을 내렸다. 열심히 stackoverflow에 질문하고 구글링한 결과 database의 설정이 다른게 맞았다. 참조하고 참조 당하는 두 테이블 간에 character set이 달랐던 것이다. 나는 바보같이, 다른 모델은 전부 utf-8로 설정해 놓고선 folder 모델을 정의할 때 character set을 utf-8로 지정하지 않았었다. 그래서 오류가 난 것이다.

로컬에서는 왜 작동을까?

내가 설정한 건지 잘 기억나진 않지만, local의 데이터베이스 테이블들은 생성될 때 기본값으로 character set이 utf-8로 설정되게 되어있었다. 반면 새로 생성한 AWS RDS는 기본값이 latin 1이었다…

해결

이를 해결하기 위해 간단히 folder모델에 charset을 정의해주었다.

const Folder = sequelize.define(
  "folder",
  {
    id: {
      type: Sequelize.UUID,
      defaultValue: Sequelize.UUIDV1,
      primaryKey: true
    },
    folderName: {
      type: Sequelize.STRING
    },
    folderCoverImage: {
      type: Sequelize.STRING
    }
  },
  {
    timestamps: true,
    charset: "utf8"
  }
);

또는 이렇게 하지 않아도 aws RDS역시 기본값을 UTF-8로 주는 방법이 있다. 여기선 기록하지 않지만 찾아보면 금방이다.

참고로 현재 데이터베이스의 기본 CHARSET값을 알고싶다면 아래와 같이 명령문을 날리면 된다.

SHOW CREATE DATABASE mydb;

참고자료

클린코드_형식 마추기

|

코드형식은 중요하다! 너무 중요해서 무시하기 어렵다. 너무나도 중요하므로 융통성 없이 맹목적으로 따르면 안 된다. 오늘 구현한 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다.

적절한 행 길이를 유지하라

500줄을 넘지 않고 대부분 200줄 정도인 파일로도 커다란 시스템을 구축할 수 있다. 반드시 지킬 엄격한 규칙은 아니지만 바람직한 규칙으로 삼으면 좋겠다. 일반적으로 큰 파일보다 작은 파일이 이해하기 쉽다.

신문 기사처럼 작성하라

이름은 간단하면서도 설명이 가능하게 짓는다. 이름만 보고도 올바른 모듈을 살펴보고 있는지 아닌지를 판단할 정도로 신경 써서 짓는다. 소스 파일 첫 부분은 고차원 개념과 알고리즘을 설명한다. 아래로 내려갈수록 의도를 세세하게 묘사한다. 마지막에는 가장 저차원 함수와 세부 내역이 나온다.

세로 밀집도

줄바꿈이 개념을 분리한다면 세로 밀집도는 연관성을 의미한다. 즉, 서로 밀접한 코드 행은 세로로 가까이 놓여야 한다는 뜻이다.

수직거리

함수 연관 관계와 동작 방식을 이해하려고 이 함수에서 저 함수로 오가며 소스 파일을 위아래로 뒤지는 등 뺑뺑이를 돌았으나 결국은 미로 같은 코드 때문에 혼란만 가중된 경험이 있는가?(있다…)

서로 밀접한 개념은 세로로 가까이 둬야한다. 타당한 근거가 없다면 당연히 서로 밀접한 개념은 한 파일에 속해야 마땅하다.

같은 파일에 속할 정도로 밀접한 두 개념은 세로 거리로 연관성을 표현한다. 여기서 연관성이란 한 개념을 이해하는 데 다른 개념이 중요한 정도다.

인스턴스 변수

인스턴스 변수는 클래스 맨 처음에 선언한다. 변수 간에 세로로 거리를 두지 않는다. 이 것은 클래스의 마지막에 놓느냐, 맨 처음에 놓느냐 하는 논쟁이 있으나 사실 그 것은 크게 중요하지 않다. 팀에 속해있다면 팀의 규칙에 따르면 되는 부분이고 중요한 것은 인스턴스 변수를 모은다는 사실이다.

종속함수

한 함수가 다른 함수를 호출한다면 두 함수는 세로로 가까이 배치한다. 또한 가능하다면 호출하는 함수를 호출되는 함수보다 먼저 배치한다. 독자는 방금 호출한 함수가 잠시 후에 정의되리라는 사실을 예측한다.

가로 형식 마추기

저자는 좋은 코드 규칙을 위해 프로젝트 7개를 조사했다. 결과가 놀랍도록 규칙적이다. 20 자에서 60자 사이인 행이 총 행 수의 40%에 달한다. 10자 미만은 30% 정도로 보인다. 80자 이후부터 행 수는 급격하게 감소한다. 프로그래머는 명백하게 짧은 행을 선호한다.

80자 제한을 말하는 사람들이 있다. 짧은 코드가 좋은 것은 맞지만, 요즘은 모니터가 크기 때문에 저자는 개인적으로 120자 정도로 행 길이를 제한한다고 한다.

가로 공백과 밀집도

가로로는 공백을 사용해 밀접한 개념과 느슨한 개념을 포현한다. 공백(‘ ‘)을 넣으면 한 개념이 아니라 별개로 보인다. 예를 들어 함수를 호출하는 코드에서 괄호 안 인수는 공백으로 분리하고 쉼표를 강조해 인수가 별개라는 사실을 보여준다. 반면 함수 이름과 이어지는 괄호 사이에는 공백을 넣지 않는다.

private void measureLine(String line) {
	lineCount++;
    int lineSize = line.length();
    totalChars += lineSize;
    lineWidhHistogram.addLine(lineSize, lineCount);
    recordWidestLine(lineSize);
}

sequelize belongsTo vs hasOne

|

Associations One-To-One

sequelize로 데이터베이스를 설계하다가, belongsTo와 hasOne의 관계가 헷갈렸다. Book과 Category 테이블이 있는데, Book은 무조건 Category를 하나 가져야만 했다. 따라서 Category는 Book에 속해있는 것이다. 그러나 따지고 보면 Book도 카테고리에 속해있다는 생각이 들었다. 그럼 결국 Book belongsTo Category인가? Book hasOne Category인가? 동시에 Category belongsTo Book을 해줘야 하나???

공식 문서 파헤치기

한번 확실히 하고 가는게 좋을 것 같아서 공식 문서를 정독했다. 따라서 이 글은 거의 공식 문서를 번역한 내용이 될 것 같다.

source vs target

공식 문서에 보면 source Model, target Model 이란 말이 나온다. User.hasOne(Project)라는 코드가 있을 때, User는 source Model이고, Project는 target Model 이다.

One-To-One 관계

1 대 1 관계는 서로 다른 두 개의 모델이 오직 하나의 foreign key로 연결 되어 있는 관계이다.

BelongsTo

belongsTo는 1 대 1 관계에서 필요한 foreign key가 source Model에 추가되는 관계(?함수?)이다. 간단한 예시로, 팀(Team)에 속해있는 선수(Player) 관계를 보자.

const Player = this.sequelize.define('player', {/* attributes */});
const Team  = this.sequelize.define('team', {/* attributes */});

Player.belongsTo(Team); // 이 경우 Player 모델에 Team모델을 참조하기 위한 TeamId라는 속성이 생긴다.

여기서 몇 가지 옵션을 줄 수 있다.

const User = this.sequelize.define('user', {/* attributes */})
const UserRole  = this.sequelize.define('userRole', {/* attributes */});

User.belongsTo(UserRole, {as: 'role'}); // userRoleId가 아닌 roleId 로 생성된다.

또한 foreignKey 옵션은 항상 기본 적으로 생성되는 foreign key 이름은 덮어 쓸 수 있다.

const User = this.sequelize.define('user', {/* attributes */})
const Company  = this.sequelize.define('company', {/* attributes */});

User.belongsTo(Company, {foreignKey: 'fk_company'}); // Adds fk_company to User

Target keys 옵션도 줄수가 있는데, 여기서 Target keys란 source 모델에서 foreign key가 가르킬 target model의 칼럼이다. 즉, target keys를 설정하면, 무조건 target Model의 primary key를 참조하는게 아니라 원하는 값을 참조 할 수 있다는 것.

const User = this.sequelize.define('user', {/* attributes */})
const Company  = this.sequelize.define('company', {/* attributes */});

User.belongsTo(Company, {foreignKey: 'fk_companyname', targetKey: 'name'}); // Adds fk_companyname to User

추가적으로 중요한 점은, User는 create 함수와 getter, setter를 얻는 다는 것이다. 공식문서에는 이렇게 나와있다

In the API reference below, add the name of the association to the method, e.g. for User.belongsTo(Project) the getter will be user.getProject().

아래 hasOne에도 나오겠지만, 둘다 getter와 setter를 가진다.

HasOne

hasOne은, foreign key가 target Model에 생긴다. 공식 문서처럼 User와 Project로 설명하자면,

// One-way associations
Project.hasOne(User)

/*
  In this example hasOne will add an attribute projectId to the User model!
  Furthermore, Project.prototype will gain the methods getUser and setUser according
  to the first parameter passed to define. If you have underscore style
  enabled, the added attribute will be project_id instead of projectId.

  The foreign key will be placed on the users table.

  You can also define the foreign key, e.g. if you already have an existing
  database and want to work on it:
*/

이렇게 설정할 경우, Project.getUser()과 Project.setUser() 사용이 가능하다. 또 한 몇가지 옵션으로,

// Or let's define some self references
const Person = sequelize.define('person', { /* ... */})

Person.hasOne(Person, {as: 'Father'})
// 이 것은 Person 모델에 FatherId 속성을 추가한다.

// also possible:
Person.hasOne(Person, {as: 'Father', foreignKey: 'DadId'})
// this will add the attribute DadId to Person

// In both cases you will be able to do:
Person.setFather
Person.getFather

foreign key는 DadId로 생기지만, orm으로 사용할 때는 as : ‘Father’를 사용했으므로, Person.getFather으로 호출할 수 있다.

(추가) 생성된 foreign Key에 데이터 넣기

예를 들어, Book과 Category 테이블이 존재하고, Book 모델에 Category를 참조할 foreign key를 가지고 싶다고 하자. 그렇다면, 위에서 보았듯이 Book.belongsTo(Category)를 하자. Book 모델에 외래키로써 CategoryId가 생긴다. 따라서 Book모델은 Book.setCategory(카테고리 인스턴스)로 외부 모듈을 참조 할 수가 있게 된다. 굳이 Book 모델을 정의할 때 CategoryId를 선언할 필요도 없고, Book.create()에서 인수로 Category인스턴스의 id를 구해서 넘겨줄 필요가 없다는 것이다.

공식문서 링크

클린코드_함수

|

작게 만들어라!

함수를 만드는 첫째 규칙은 ‘작게!’다. 함수를 만드는 둘째 규칙은 ‘더 작게!’다.

블록과 들여쓰기

if 문/else 문/while 문 등에 들어가는 블록은 한줄이어야 한다. 대게 거기서 함수를 호출한다. 그러면 바깥을 감싸는 함수가 작아질 뿐 아니라, 블록 안에서 호출하는 함수 이름을 적절히 짓는다면, 코드를 이애하히고 쉬워진다. 함수에서 들여쓰기 수준은 1단이나 2단을 넘어서면 안된다. 그래야 함수는 읽고 이해하기 쉬워진다.

한 가지만 해라!

함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.

지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한가지 작업만 한다.

함수가 ‘한 가지’만 하는지 판단하는 방법이 하나 더 있다. 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.

위애서 아래로 코드 읽기: 내려가기 규칙

코드는 위에서 아래로 이야기처럼 읽혀야 좋다.

서술적인 이름을 사용하라!

좋은 이름이 주는 가치는 아무리 강조해도 지나치지 않다. 워드가 말했던 원칙을 기억하는가? “코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다.” 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.

이름이 길어도 괜찮다. 길고 서술적인 이름이 짧고 어려운 이름보다 좋다. 길고 서술적인 이름이 길고 서술적인 주석보다 좋다. 이름을 정하느라 시간을 들여도 괜찮다. 이런저런 이름을 넣어 코드를 읽어보면 더 좋다.

함수 인수 (따끔)

함수에서 이상적인 인수 개수는 0개다. 다음은 1개(단항)고, 다음은 2개(이항)다. 3개 이상은 가능한 피하는 편이 좋다. 4개 이상은 특별한 이유가 필요하다. 특별한 이유가 있어도 사용하면 안된다(뜨아…내 코드를 당장 다시 봐야겠다).

인수는 어렵다. 인수는 개념을 이해하기 어렵게 만든다. 코드를 읽는 사람에게는 incluedSetupPageInto(new PageContent)보다 incluedSetupPage()가 이해하기 더 쉽다.

테스트 관점에서 보면 인수는 더 어렵다. 갖가지 인수 조합으로 함수를 검증하는 테스트 케이스를 작성한다고 상상해보라. 인수가 없다면 간단하다. 인수가 하나라도 괜찮다. 인수가 2개면 조금 복잡해진다.

플래그 인수

플래그 인수는 추하다. 함수로 boolean값을 넘기는 관례는 정말로 끔찍하다. 왜냐고? 함수가 한꺼번에 여러 가지가지를 처리한다고 대놓고 공표하는 셈이니까! 플래그가 참이면 이걸 하고 거짓이면 저걸 한다는 말이니까!

이항 함수

인수가 2개인 함수는 인수가 1개인 함수보다 이해하기 어렵다. 예를 들어, writeField(name)은 writeField(outputStream, name)보다 이해하기 쉽다. 둘다 의미는 명백하지만 전자가 더 쉽게 읽히고 더 빨리 이해된다.

동사와 키워드

단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다. 또한 함수 이름에 키워드를 추가하는 것도 좋다. 즉, 함수 이름에 인수 이름을 넣는다. 예를 들어, assertEquals보다 assertExpectedEqualsActual(expected,actual)이 더 좋다. 그러면 인수 순서를 기억할 필요가 없어진다(요즘엔 ide에서 마우스 hover만 해도 알려주긴 한다..).

명령과 조회를 분리하라! (내가 잘 지키지 못하는 것..)

함수는 1. 뭔가를 수행하거나 2. 뭔가에 답하거나 둘 중 하나만 해야 한다. 둘 다 하면 안된다. 객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다.

if (set("username", "unclebob"))...

독자 입장에서 코드를 읽어보자. 무슨 뜻일까? “username”이 “unclebob”‘으로 설정되어 있는지 확인하는 코드인가? 아니면 “username”을 “unclebob”으로 설정하는 코드인가? 함수를 호출하는 코드만 봐서는 의미가 모호하다. “set”이라는 단어가 동사인지 형용사인지 분간하기 어려운 탓이다.

해결책은 명령과 조회를 분리해 혼란을 애초에 뿌리뽑는 방법이다.

if (attributeExists("username")) { // 2. 반환하거나(답하거나)
  setAttribute("username", "unclebob"); // 1. 수행하거나
}

오류 코드보다 예외를 사용하라!

오류가 날 것이라면, 오류 코드를 보내는 것보단 try catch를 사용하라. 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.

정리

변수명 편에서는 나름 따끔한 내용들이 많지 않았다고 생각했는데, 함수 편에서는 따끔한 내용들이 여럿 보였다. if문이 깊어진다던지 명령과 조회를 한번에 묶어서 코드를 짠다던지 오류 코드를 반환한다던지…

책의 마지막에도 나오지만 처음부터 코드를 깔끔하게 작성할 수는 없다. 누구든지 처음에는 길고 복잡하다. 이 책에서 나온 안좋은 예시들에 해당하는 코드들이 상당히 많지만, 계속해서 꾸준히 가다듬고, 변수명을 다시 짓고, 인수를 줄이고 하는 과정을 거치다 보면 어느새 훌륭한 코드가 완성되어 있을 것이다.

무엇 보다 중요한 것은, 좋은 코드에 대한 생각을 품은 상태에서 코드를 짠다는 점인 것 같다. 매번 코딩할 때 마다 이 코드가 좋은 코드일까? 더 좋은 코드를 만들 수 없을까? 를 생각할 수 있느냐 없는 냐의 차이가 좋은 코드를 만드는 시작점이라는 생각이 든다.