[유틸리티] 한글 자소 교정기

한글 자소 교정기

맥과 윈도우 환경에서 각각 생성된 파일을 서로 교환하는 일이 많아지면서 한글 파일명에서 자소가 풀어지는 현상이 자주 발생한다. 주로 윈도우 환경에서 일하다 보니 맥에서 만들어진 파일의 이름이 깨진 경우가 많아서 고민하다가 인터넷에서 찾아보니 아래에 링크한 프로그램을 이용하면 파일명을 곧바로 원래대로 되돌릴 수 있었다.

나 같은 경우에는 아예 다운로드 폴더에 이 프로그램을 넣어두고 자소 교정을 해야 할 때마다 파일 경로로 현재 디렉터리를 의미하는 . 을 입력해서 파일을 찾곤 한다. 그럼 현재 디렉터리가 다운로드 폴더이기 때문에 다운로드한 파일을 바로 찾을 수 있고 번거롭게 경로를 찾아들어가야 하는 불편함이 없다.

유용한 프로그램을 만들어 주신 제작자분께 다시 한 번 감사드린다.


2018년 10월 16일 업데이트

앞서 안내해 드린 프로그램의 작동이 원활하지 않은 경우 다음 글을 참고하셔서 다른 유틸리티를 사용해 보시기 바랍니다. 🙂


2019년 2월 17일 업데이트

이 프로그램의 제작자분께서 새 버전을 제작해서 배포 중입니다. 아래 URL을 통해 프로그램을 내려받아 사용하세요!

2020년 10월 05일 업데이트

이 프로그램의 제작자분께서 macOS 버전을 제작해서 배포 중입니다. 아래 URL을 참고하세요!

지적 대화를 위한 넓고 얕은 지식: 철학, 과학, 예술, 종교, 신비 편

요즘 통 책을 못 읽다가 그동안 조금씩 읽어왔던 이 책을 이번에 마저 읽었다. 1편과 마찬가지로 철학, 과학, 예술, 종교, 신비 편을 주제로 한 이 책은 절대주의, 상대주의, 회의주의라는 세 가지 기조를 가지고 철학 등의 분야의 역사를 깔끔하게 정리해낸다. 그리고 역시나 마찬가지로 이름이나 세부적인 입장만 달리할 뿐 세 가지 기조가 그동안의 역사에 걸쳐 계속 엎치락뒤치락하면서 이어져 왔음을 알 수 있었다.

이 시리즈의 책을 읽으면서 얻은 가장 큰 수확은 복잡다단한 세계를 관통하는 몇 가지 준거의 틀을 갖게 됐다는 것이다. 이번 편에서는 세 가지 기조를 토대로 세상을 바라보고 이해할 수 있었고, 1편에서는 경제적인 측면에서 비롯되는 세상의 현상들을 좀 더 유기적으로 이해할 수 있게 됐다는 것이다.

그동안 학교 교육을 받으면서 배웠던 각종 사회 현상이나 역사적 사건들을 좀 더 체계적으로 이해하고 이면에 놓인 배경과 의의를 확실하게 파악할 수 있게 됐다는 점에서 후한 점수를 주고 싶다. 단순히 지적인 대화를 위한 목적도 목적이지만 세상을 이해하는 하나의 체계적인 틀을 마련할 수 있다는 점에서 중고등학교 과정을 시작하는 학생들부터 이런 책을 읽을 수 있게 해준다면 좋을 것 같다. 학창시절에 이런 책을 만났었더라면 좀 더 공부하기가 수월하고 재미있었을 텐데, 하는 아쉬움이 있다.

메서드명은 주의 깊게 골라라

참고: 이 글은 Elegant Object 책의 샘플 PDF로 제공되는 내용을 저자의 허락하에 번역한 것입니다.


2.4 메서드명은 주의 깊게 골라라

이미 1.1 절에서 클래스 이름을 지정하는 방법을 살펴봤다. 이제 메서드명을 적절히 지어볼 시간이다. 가장 간단하고도 중요한 규칙은 다음과 같다: 빌더(builder)는 명사이고, 조종자(manipulator)는 동사다. 이것이 무슨 뜻인지 알아보자.

무언가를 만들어 새로운 객체를 반환하는 메서드를 빌더라고 한다. 이 이름은 그냥 내가 만들긴 했지만 나에게는 논리적인 이름으로 보인다. 빌더는 항상 뭔가를 반환한다. 빌더는 절대 void를 반환하지 않으며, 빌더의 이름은 항상 명사다.예를 들면 다음과 같다.

int pow(int base, int power);
float speed();
Employee employee(int id);
String parsedCell(int x, int y);

마지막 메서드인 parsedCell()을 눈여겨보자. 이 메서드는 명사는 아니지만 앞에 형용사가 붙어있다. 이렇게 해도 원칙을 위배하지는 않는다. 단지 이름을 좀 더 자세히 설명할 뿐이다. 이 이름은 여전히 명사지만 이 메서드에 관한 정보를 더 담고 있다. 단순히 셀이 아니라 파싱된 셀인 것이다. 우리는 이 메서드가 어떤 식으로든 내용을 변형한 셀을 반환하리라 예상할 것이다.

객체를 통해 추상화된 현실 세계의 개체에 변형을 가하는 메서드를 조종자라고 한다. 조종자는 항상 void를 반환하고 그것의 이름은 늘 동사다. 예를 들면 다음과 같다.

void save(String content);
void put(String key, Float value);
void remove(Employee emp);
void quicklyPrint(int id);

마지막 메서드인 quicklyPrint()를 눈여겨보자.이 메서드의 이름은 앞에 부사가 붙은 동사다. 여기서 핵심적인 부분은 “print”라는 동사이고, “quickly”는 단순히 이 동사를 설명해줌으로써 이 메서드의 문맥과 목적에 관한 추가 정보를 준다는 것이다.

다시 말하지만, 이러한 빌더와 조종자는 이번 장의 내용에 맞춰 내가 만들어낸 용어다. 빌더와 조종자를 다른 식으로 불러도 되지만 ‘빌더는 무언가를 만들고 조종자는 조작한다’라는 원칙은 온전히 지키려고 노력해야 한다. 그리고 빌더와 조종자 사이에는 아무것도 없다. 무언가를 조작하고 반환하는 메서드뿐 아니라 무언가를 만들고 동시에 조작하기도 하는 메서드가 있어서는 안 된다. 몇 가지 나쁜 예를 들어 보겠다.

// 저장된 전체 바이트 수를 반환
 int save(String content);

// 맵이 변경됐다면 TRUE를 반환
 boolean put(String key, Float value);

// 속도를 저장하고 이전 값을 반환 
float speed(float val);

나중에 3.5절에서 “설정자(setter)”와 “접근자(getter)”에 대해 살펴보겠지만 여기서는 이미 get으로 시작하는 이름이 잘못됐다는 점이 훤히 드러난다. 그 이유는 “get”은 동사지만 접근자 메서드는 기본적으로 뭔가를 반환하도록 만들어진 빌더이기 때문이다. 따라서 이것이 바로 “접근자” 메서드에 대한 나의 첫 주장이다.

이제 이 아이디어에 대해 설명해야 할 것 같다. 이 주장에 찬성하는 몇 가지 논거들이 있다.

2.4.1 빌더는 명사다

우선 메서드에서 뭔가를 반환할 경우 메서드명을 동사로 짓는 것은 잘못됐다고 생각한다. 이러한 이름은 객체 중심의 사고와 충돌한다. 내가 제과점에 들렀다면 “브라우니 하나 만들어 주세요”라거나 “커피 한 잔 끓여주세요”라고 말하지 않는다. 그 대신 “브라우니 하나 주세요”나 “커피 한 잔 주세요.”라고 말할 것이다. 뭔가를 “만들어 주세요”라거나 “끓여주세요”라고 말한다면 말투가 다소 공격적으로 느껴질 것이다. 브라우니가 정확히 어떻게 만들어지거나 커피가 정확히 어떻게 끓여지는지 신경 써서는 안 된다. 그것들을 어떻게 만들지는 제과점에서 신경 쓸 일이다. 나는 객체(브라우니나 커피)를 요구하고 있다. 제과점은 내 요구를 충족시킬 수 있다. 제과점 내부에서 이 같은 일이 정확히 어떻게 일어나는지는 내가 상관할 바가 아니다. 다음은 제과점을 코드로 표현한 예다.

class Bakery {
  Food cookBrownie();
  Drink brewCupOfCoffee(String flavor);
}

위의 두 메서드는 사실 객체의 메서드가 아니다. 이 두 메서드는 절차(procedure)에 해당한다. 제과점에서 명명한 이름을 통해 우리는 제과점을 하나의 자족적이고 자주적인 객체로서 존중해야 하고 제과점이 어떤 역할을 하는지 이해할 수 있다. 이것은 절차적인 접근법이지 객체 지향적인 접근법이 아니다. 다음은 이 두 가지 절차가 C에서는 어떻게 설계될지 보여주는 예다.

Food* cook_brownie() {
  // 브라우니를 만듬
  // 만든 브라우니를 반환
}

Drink* brew_cup_of_coffee(char* flavor) {
  // 커피를 끓임
  // 끓인 커피를 반환
}

제과점이 아무데도 관여하지 않는다. 단순히 C 문법으로 된 두 개의 기계어 명령이 있고 그것들을 호출할 뿐이다. 이를 C 언어에서는 함수라고 하지만 함수는 함수형 프로그래밍과는 거의 무관하기 때문에 실제로는 절차에 해당한다. 우리는 컴퓨터에게 이러한 명령어를 실행해서 그 결과를 반환하도록 요청한다. 이것은 컴퓨터처럼 생각하는 것이지 객체처럼 생각하는 것이 아니다. 우리는 제과점을 신뢰하지 않기 때문에 마시고 싶은 커피를 요청한 다음 결과물이 만들어지도록 맡기는 대신 “커피를 끓여달라고”라고 주문하는 것이다.

너무 철학적인 이야기처럼 들리고 싶진 않지만 이러한 이름 짓기라는 주제는 사실 매우 추상적이고 개념적이다. 적절히 명명된 메서드는 사용자가 객체의 설계 목적과 사명, 존재 목적을 비롯해 객체에게 삶의 의미란 무엇인가를 더 잘 이해하도록 도와준다. 반면 부적절한 메서드명은 객체의 전체적인 인상을 망칠 수도 있고 사용자로 하여금 단순히 객체를 데이터 보관함이나 절차의 모음으로 여기게 만든다. 이것은 OOP 라이브러리, SDK, API 등에서 반복적으로 일어나는 아주 전형적인 실수다. 객체는 계약에 따라 동작하고 싶어하지 단순히 명령을 따르기만을 원하지는 않는다. 이 둘 사이에는 큰 차이점이 있다.

이 같은 이유로 메서드의 이름이 동사라면 그것은 기본적으로 객체가 “어떤 일을 할 것인가”를 알려준다. 그리고 객체에게 무언가를 “만들라”고 요청하는 것은 객체를 활용하는 공손하고 존중하는 방법이 아니다. 단지 만들고자 하는 것을 요청하기만 하고 그것을 어떻게 만들지는 객체가 결정하도록 내버려두자. 다음과 같은 이름은 모두 잘못된 것이다.

InputStream load(URL url);
String read(File file);
int add(int x, int y);

모두 다음과 같은 이름으로 바꿔야 한다.

InputStream stream(URL url);
String content(File file);
int sum(int x, int y);

add(x,y) 대신 sum(x,y)을 사용하도록 제안한다는 점을 눈여겨보자. 사소하고 별로 중요하지 않은 변화로 보일 수도 있지만 실제로 사고하는 데 큰 차이를 빚어낸다. 우리는 객체가 xy에 더하도록 요청하지 않는다. 대신 객체가 두 값의 합을 만들어서 새로운 객체를 반환하도록 요청한다. 그 객체가 실제로 합계를 알아낼까? 알 수 없다. 아마도 그럴 테지만. 내가 유일하게 아는 것은 결과가 xy의 합계처럼 보일 것이라는 점이다. 다시 말하지만, 나는 객체가 무엇을 하라고 말하는 게 아니라 결과가 특정 계약, 즉 정수형 숫자라는 것을 준수해야 함을 요청하는 것이다.

이것이 바로 나의 첫 번째 주장이자 첫 번째 사용 예다. 우리는 객체로부터 뭔가를 받고 있는데, 다시 말해 객체로 하여금 무언가를 만들도록 요구하고 있다. 이제 우리가 객체로 하여금 무언가를 조작하도록 요청할 때 일어나는 두 번째 논쟁거리이자 사용 사례로 넘어가자.

2.4.2 조종자는 동사다

기억하고 있다시피 객체는 현실 세계의 개체를 표현한 것이다. File 클래스의 객체는 디스크상의 파일을 표현하고, Pixel 클래스의 객체는 화면상의 픽셀을 표현하며, Integer 클래스의 인스턴스는 램의 4바이트를 나타낸다(놀랐는가? 이 주제에 대해서는 3.4절에서 좀 더 자세히 살펴보겠다).

현실 세계의 개체를 조작해야 하는 경우, 우리는 객체가 그렇게 하도록 요청한다. 다음 예제를 보자.

class Pixel {
  void paint(Color color);
}
Pixel center = new Pixel(50, 50);
center.paint(new Color("red"));

예제에서는 center 객체가 50×50 좌표에 위치한 픽셀을 칠하도록 요청하고 있다. 이 경우 아무것도 생성되지 않을 것으로 예상한다. 단지 우리는 무언가에 변화를 가하고 싶고 객체는 그것을 표현한다. 이제 이것이 어떻게 절차가 아닌지 의문이 들지도 모른다. 이것은 이름이 동사로 돼 있고, 기본적으로 객체가 우리 대신 무언가를 하도록 지시하고 있다. 그렇다, 좋은 질문이지만 핵심적인 차이점은 반환된 결과다.

paint() 메서드는 결과를 반환하지 않는다. 제과점 은유를 사용하자면 이것은 바텐더에게 음악 볼륨을 높여달라고 부탁하는 것과 비슷하다. 바텐더가 음악을 더 크게 틀 것인가? 아마 그렇게 할 것이다. 어쩌면 그렇지 않을지도 모른다. 우리의 요청은 그냥 무시될 수도 있다. 이 경우 우리에게 무언가가 되돌아오리라 예상하지 않으므로 이것은 공격적이거나 무례한 것이 아니다. 앞서 부탁한 내용을 다른 식으로 표현하면 어떻게 들릴지 상상해 보자. “음악을 키워주시고 볼륨을 키우고 나면 볼륨이 얼마나 되는지 저한테 말해주십시오.” 값을 반환하는 조종자는 정확히 이런 모습이다. 굉장히 무례한 것이다.

따라서 차이점은 바로 반환값에 있다. 오직 빌더만이 값을 반환할 수 있고, 빌더의 이름은 반드시 명사여야 한다. 객체가 우리로 하여금 무언가를 조작하도록 허용할 경우 그 객체의 이름은 동사여야 하고 반환값이 없어야만 한다.

이 같은 주요 원칙을 염두에 둔다면 조금 덜 엄격한 명명 관례는 준수하는 것이 가능하다고 생각한다. 예를 들어, 빌더 패턴을 사용할 경우 메서드명 앞에 with 접두사를 둘 수 있다.

class Book {
  Book withAuthor(String author);
  Book withTitle(String title);
  Book withPage(Page page);
}

예제에서 withTitle이라는 이름은 bookWithTitle을 짧게 줄인 형태다. 모든 메서드에서 이러한 book 접두사를 생략하려면 with 접두사를 쓰기만 하면 된다. 그렇지만 원칙은 여전히 지켜지고 있다. 즉, 이러한 메서드는 빌더이고, 빌더의 이름들은 명사로 분류되고 있는 것이다.