잘 만들어진 코드란 요구사항을 정확히 만족하는 코드이다. 그런데 현실에선 요구사항들이 끊임없이 변하기 때문에 만족시키기란 쉽지 않다. 결론적으로 변화하는 요구사항을 안정적으로 잘 동작시키는 코드가 잘 짠 코드라고 말 할 수 있다. 그런 의미에서 객체지향이 관심의 대상이 되고 각광받는 이유는 구조적 프로그래밍으로 대변되는 기존의 방식들에 비해 요구사항을 보다 유연하고 안정적으로 만족시키기 때문이다.


개발하기에 가장 좋은 방법은 수준 높은 개발자들 여럿이서 서로 원하는 수준에서 활발히 커뮤니케이션을 하면서 일하는 것이다. 하지만 우리가 일하는 현실을 돌아보자. 회사 내에서 모든 개발자가 같은 수준에서 개발할 수는 없다. 중급 개발자와 초급 개발자가 똑같은 수준에서 개발을 할 수는 없을 것이다.


통일성이나 수정의 용이성 같은 말은 사실 아주 쉬운 의미이다. 함수들의 공통점을 묶고 나아가 클래스간의 공통점을 묶고 컴포넌트간의 공통점을 묶고 이렇게 잘 묶어서 정리하다보면 그게 바로 객체지향 프로그래밍이고 컴포넌트 프로그래밍이다. 물론 프로그램의 크기가 커질수록 묶는 개념도 커지고 요구사항이나 제품의 방향에 따라 고려해야할 것도 많아지지만 언제나 기본에 충실하라. 공통점 묶기가 객체지향의 초석임을 잊지 말자.


공통점 묶기와 조금만 알기는 객체지향 언어에서 상속, 다형성, 캡슐화보다 더 중요한 개념이다. 공통점을 묶고 조금만 알기 위해 노력하다 보니 상속, 다형성, 캡슐화가 필요하게 되고 더불어 추상화(Abstaction), 일반화(Generalization)라고 세분화(Specialization)도 이루어지는 것이다.

  모든 이론이나 개념은 경험적 발전과 필요에 의해 탄생한 것이고 왜 필요하게 된 것인지를 이해하는 것이 단순히 그 정의와 내용을 공부하는 것보다 훨씬 중요하다. 객체지향의 결정판이라고 알려진 디자인 패턴을 어렵게 느끼는 이유 중 하나는 경험의 산물로 보지 않고 공부해서 익혀야 되는 것으로 인식하기 때문이다. 또 캡슐화에 관해 모든 변수를 반드시 private으로 선언해야 한다는 법이 있는 것은 아니다. 모든 개발자가 어떤 변수는 직접 접근하고 어떤 변수는 접근하면 안된다는 것을 안다면 왜 안되겠는가? 하지만 현실적으로 모두가 똑같은 지식과 레벨에서 개발할 수 없으므로 언어가 이런 점들을 지원해주는 것이다.

  언어는 개발자의 실수를 언어가 최대한 막아주어 생산성을 높이는 방향으로 발전한다는 점을 기억하자.


  모든 클래스는 하나의 책임만을 가진다.


  "이 클래스가 무슨 일을 하는 클래스입니까?" 라는 질문에 "이 클래스는 A라는 일을 합니다." 라고 간단히 대답할 수 있어야 한다. "이 클래스는 맑은 날엔 A라는 일을 하고 비오는 날엔 B라는 일을 하고 안개 낀 날에는 C라는 일을 합니다."라고 대답한다면 클래스의 의미를 정확히 파악할 수 없다. 우리가 클래스를 만드는 이유가 관련된 데이터를 하나로 묶어서 이해하기 쉽도록 하는 것인데 클레스가 너무 많은 책임을 가지면 이해하기가 쉽지 않다. 만약 이렇게 여러가지 책임을 가져야 한다면 클래스를 나눌 것을 강력 추천한다. 객체지향에서 제일 어려운 일 중 하나는 관련된 정보로만 클래스를 구성하는 일이다. 변수 하나, 함수 하나를 넣을 때 "과연 이 기능이 이 클래스에 들어가는 것이 맞을까?"에 대해 고민해야 한다. 클래스 하나가 이일 저일 다하게 되는 신(GOD) 클래스가 된다면 그건 그냥 거대한 코드 덩어리일 뿐 어떠한 방법론의 코드도 아니다.

  클래스는 하나의 책임만을 가진다라는 대명제를 중심에 놓는다면 상속으로 이루어진 클래스 트리 전체는 하나의 책임으로 묶인다고 볼 수 있다. 이런 이유로 Java는 단일 상속만을 지원하지만 부족함이 없는 것이다(Java의 상속은 extends 키워드를 사용한다).


  인터페이스 상속(Java에서는 implements)에 대해 알아보자. 앞서 클래스는 하나의 책임을 가짐과 동시에 상속받는 클래스 트리는 하나의 책임으로 묶여진다고 이야기 했다. 하지만 C++의 다중 상속이나 Java의 인터페이스 구현은 필요하기 때문에 존재하는 것이다. 여기서 혼란이 올 수 있다. 두 클래스에서 상속을 받으면 상속받은 자식 클래스는 두 개의 책임을 지는 것이 되니 하나의 책임을 지는 룰에 위배된다고 생각할 수 있다. 하지만 클래스 상속과 인터페이스 상속은 그 의미가 분명히 다르다. 클래스 상속은 분명 부모와 자식간에 같은 책임을 가지지만 작동하는 형태가 다른 것을 의미한다. 반면 인터페이스 상속기능을 추가하거나 클래스 간의 통신을 하기 위한 방법을 제공한다는 의미이다.

  모든 클래스는 하나의 책임만을 가지고 이런 클래스들이 서로 통신하면서 동작하는 것이 객체지향 프로그래밍이다. 이 때 클래스들이 만나는 방법(사전에서 경계면, 접점으로 표현한 부분)을 인터페이스라고 부른다.   Java에서는 모든 함수가 가상 함수(Virtual Function)로 동작하므로 virtual 키워드를 붙이지 않아도 된다. 어쨌든 Java에서는 class 키워드 대신에 더 의미가 분명한 interface 키워드를 제공한다.


  객체지향에서는 클래스 하나를 작성하는 것보다 클래스 사이의 관계를 작성하는 것이 더 중요하다. 클래스 자체가 문제라면 문제 범위를 클래스로 좁힐 수 있지만 클래스 사이의 관계가 문제가 되면 관련된 모든 클래스의 동작 방식이 문제가 되기 때문이다.


  완전한 클래스와 인터페이스 사이에 있는 개념이 추상 클래스이다. 추상 클래스는 인터페이스 개념을 잘 이해하였다면 쉽게 이해할 수 있다. 추상 클래스는 그 이름에서 알 수 있듯이 클래스의 한 종류이다. 하지만 클래스 전체를 다 구현하지 않고 자식 클래스에서 구현의 일부를 위임하기 때문에 실제(Concrete) 클래스라고 부르지 않고 추상(Abstract) 클래스라고 부르는 것이다. Java에서는 추상 클래스를 위한 키워드로 abstract라는 키워드를 지원하여 의미를 더욱 분명히 하였다. 엄밀히 따지면 인터페이스를 작성할 때 구현을 하지 않는 것을 원칙으로 한다. 하지만 인터페이스를 잘 정의하지 못한 상황에서 인터페이스 구현을 하다 보면 자식 클래스들에서 중복으로 구현되는 기능들이 있다. 이런 기능들의 공통점 묶기를 써서 부모 클래스, 즉 인터페이스에 일부 구현하는 경우가 있다. 이 때 java에서는 명백히 인터페이스를 추상 클래스로 변경하도록 컴파일러가 요구한다. 정석대로라면 추상 클래스는 상속 계층에서 책임을 지는 부모 클래스로만 만드는 것이 좋고 인터페이스는 다른 클래스와의 관계만을 기술하므로 구현이 없는 것이 좋다. 하지만 개발을 하다 보면 편의상 인터페이스에 일부 구현을 할 수 있으며 이때는 구현이 있다고 추상 클래스로 보는 것보다는 원래 목적대로 인터페이스로 보는 것이 타당하다.

 

정리

  객체지향 프로그래밍의 기본 단위는 클래스이다. 모든 클래스는 단 하나의 책임을 가지는 것이 좋다. 레고 부품을 조립하는 것처럼 각자의 책임을 가지는 클래스를 우리가 어떻게 조립하는가에 따라 결과물이 달라진다. 객체지향을 통한 재사용의 강력함은 이 분해와 조립에 있다고 할 수 있다. 클래스를 조립할 때 서로 다른 책임의 클래스를 연결하는 방법은 클래스들을 직접적으로 연관시켜서 할 수 있지만 이 방법은 결합도와 복잡도를 증가시키므로 좋은 방법이 아니다. 결합도와 복잡도는 클래스를 연결시켜주는 인터페이스 사용을 통해 낮출 수 있다. 한편 결합도와 복잡도를 낮춘다는 의미는 확장성을 높인다는 의미로 해석할 수 있다. 인터페이스도 클래스 사이를 연결시켜 주는 책임을 가지는 또 다른 클래스의 형태라고 할 수 있다.

이 글은 스프링노트에서 작성되었습니다.

업계에서는 경력자를 요구한다. 아직 검증되지 않은 신입을 발굴하여 고용하는 비용보다는 경력자를 고용하는 것이 더 비용 대비 효과가 좋은 것으로 판단을 하고 있는 것으로 보인다. 그게 틀린 말은 아니라는 생각도 든다. ㅡㅅ-)>

하지만 초보자로서 경력을 쌓아할 수 있는 인재를 발굴하려는 노력도 해줘야하지 않을까?

내가 개발자 신입으로 취업을 하지 못하는 상황이기 때문에 하는 이야기(OTL) 이기도 하지만, 그런 노력이 부족한 것도 사실이라고 생각한다. 현재 나에게 중요한 것은 3년을 비비고살 든든한 산을 구하는 일이 아닐까 생각된다. 

몇번의 면접을 보고나서 입사제의를 거절한 짧은 경험을 바탕으로 조금 적어보고자 한다.

프로그래머 신입 개발자로서 준비해야할 것
  • 자신감 - 어디에 가서든 내 역활을 제대로 수행할 수 있다.
  • 도전정신 - 새로운 것에 대한 도전정신, 그것은 사회 초년병이 가져야할 무기다.
  • 기술력 - 프로그래머로서의 기본은 어디까지나 프로그래밍 능력이다. 코등 스타일, 프로그래밍에 대한 개념을 깨우치자.
  • 친화력 - '다른 사람들과 얼마나 친하게 어울릴 수 있는가' 하는가를 주의깊게 보는 것 같다.
  • 인내심 - 원래 노동 자체가 어려운 일이다. 2~3년 이상을 한 직장에서 꾸준하게 버틸 수 있는 인내심이 필요하다. 2~3년 뒤에는 당신을 찾는 이들이 많은 것을 경험하게 될 것이다. 지금 구인 정보를 봐도 2~3년 경력직을 찾는 이들이 많다.
  • 자격증 - SCJP, OCP 같은 국제공인 자격증은 모르겠다. 하지만 반드시!! '정보처리기사, 산업기사' 자격증을 취득하라.
  • 이력서 - 자신이 가지고 있는 것을 제대로 표현할 수 있는 이력서 양식을 갖추어라.
    • 컨설턴트는 입사만 하면 된다라는 이야기를 한다. 그게 사실일지 모른다. 그럴 생각이라면 제대로 꾸며쓰고 제대로 외우고 자신의 것으로 만들어라. '입사'라는 목적을 위한 수단으로 사용하는 이력서를 제대로 자신의 것으로 숙지하라.
  • 자기PR - 현재는 자기를 제대로 광고하고 자신을 상품화할 수 있는 능력을 필요로 한다.
    • 개인 블로그, 위키, 포트폴리오 기록 등을 남기는 것을 게을리하지 말자.

아직도 나는 구직을 희망하고 있는 백수!!

빠른 시일 안에 백수를 타파하는 것이 나의 목표!! +_+)b 9월은 취업의 달!!

'Programming' 카테고리의 다른 글

[펌] 2010년 전략 기술 톱 10  (0) 2009.10.26
잘 만들어진 코드  (0) 2009.09.07
코딩 스타일  (0) 2009.08.24
Adapter 패턴  (0) 2009.08.18
허니몬, 새삼 느끼다. 공부한 것의 정리에 대해서.  (0) 2009.05.12

코딩 스타일이란 무엇인가


작성자 : 김지헌


단위프로그램 작성 과정

1. 문제를 인식한다.

2. 해법을 찾는다.

3. 해법을 플로 차트나 의사 코드로 표현한다.

4. 프로그래밍 언어로 원시 코드를 작성한다.

5. 작성한 원시 코드를 컴파일하여 목적 코드로 바꾼다.

6. 단위 프로그램을 테스트하고 오류를 수정한다.


coding_style.JPG

코딩은 프로그래밍과 거의 동일하게 사용된다. 하지만, 엄밀한 의미에서 보면 프로그래밍은 프로그램의 논리를 개발하는 일이고, 코딩은 프로그램의 논리에 따라 특정 프로그램 언어로 표현하는 일이라고 할 수 있다.


그리고 코딩하는 단계에서 다음과 같은 작업에 대한 프로그래머마다의 고유한 방식을 코딩 스타일(Coding Style)이라고 한다. 코딩 스타일과 유사한 개념으로 ‘코딩 컨벤션(coding convention)’이라는 것이 있다. 번역하면 ‘코딩 습관’이라는 말이다. 코딩 스타일과 코딩 컨벤션은 거의 동일한 의미다.

 

1. 논리를 표현하는 방식

2. 알고리즘을 표현하는 방식

3. 문장(statement)를 배치하는 방식

4. 표현식(expression)을 표현하는 방식


플로 차트(flow chart)

플로 차트는 프로그램의 흐름을 한눈에 파악하기 좋은 도구다. 하지만, 프로그램을 코딩하는 시간보다 플로차트를 그리는 시간이 더 걸리기 일쑤다. 능숙한 프로그래머들은 플로 차트 없이도 프로그램의 흐름을 쉽게 간파한다. 그리고 최신 개발 도구들은 플로 차트보다 더 생산성이 높은 분석 도구들을 제공한다.

의사코드(pseudo code)

특정 프로그래밍 언어를 동원하지 않고, 우리가 일상적으로 쓰는 언어를 동원하여 프로그램의 논리를 표현한 것이다. 어떤 면에서 프로그램보다 더 프로그램을 잘 설명한다.

한마디로 표현하면, ‘원시 코드를 작성하는 방식 = 코딩 스타일’ 이라고 할 수 있다. ^^


오류 종류

오류 설명

논리 오류

논리적인 결합이 잠재되어 있다가 특정 상황에서 엉뚱한 방향으로 프로그램이 흐른다.

연산 오류

특히 실수형 연산에서 발생한 오차가 누적되어 계산상의 거대한 오차를 불러온다. ‘나비 효과’가 발생한다.

호출 오류

라이브러리로 제공되는 함수들이 함수 제작자도 알지 못하던 오류가 숨어 있어서 그것을 호출한 프로그램이

잘못 작동한다.

결합 오류

함수의 호출, 클래스의 상속, 컴포넌트간의 결합에 있어서 인수, 복귀 자료의 자료형이나 값이 잘못 교환된다.

환경 오류

프로그램을 제작할 당시에 가정하였던 실행 환경이 바뀌면 예상하지 못한 결과를 가져온다.

입력 오류

프로그래머가 예상하지 못한 엉뚱한 값이 입력된다.

결국 계산상의 오류를 가져온다.

작동 오류

프로그램이 정해진 순서에 따라 작동하지 않는다.

발전소와 같은 대규모 공정 제어 중에 큰 문제를 일으킨다.



코딩 스타일의 목표

1. 개발 기간을 지연시키는 것을 예방한다.

2. 유지보수를 쉽게 한다.

3. 오류 없는 정확한 프로그램을 만든다.


코딩 스타일은 프로그래밍에 있어서 수학이나 영어보다 중요하다.

프로그래머들조차 프로그래머에 대한 잘못된 편견을 가지고 있다. 그 중에서 가장 흔한 것으로는, 프로그래머가 되려면 수학을 잘해야 한다는 편견이다. 물론 수학과 영어를 잘 하면 프로그램을 작성하는 데 도움이 된다. 수학을 잘하면 프로그램의 알고리즘을 잘 세울 수 있을지는 모르지만, 모든 프로그램에 고난이도의 알고리즘이 필요한 것은 아니므로 수학을 잘 한다는 것이 곧 좋은 프로그램을 작성하는 것을 보장하는 것은 아니다. 오히려 대부분의 프로그램 논리는 단순하며, 이미 잘 알려진 알고리즘을 함수의 형태로 제공받아 사용한다.
프로그래머들은 영어로 대화하는 것이 아니라 프로그램으로 대화한다. 영문법에 맞는 매끄러운 영어를 구사하는 사람들이 인정받듯이, 코딩 스타일 규칙에 따라 프로그램을 작성하는 사람이 인정받을 수 있다. 그런 사람은 다른 프로그래머와의 협업에서도 환영받을 것이다.

 

● 띄어 쓸 때 좋은 코딩 습관

 

한 줄에 한 문장만 써라.

  1. System.out.println("첫 번째 수는 1입니다."); System.out.println("두 번째 수는 2입니다."); System.out.println("세 번째 수는 3입니다.");

여기서 줄(line)이란 편집기(editor) 상에서 보이는 한 줄을 의미한다. Java의 문장이 한 줄은 아니다. 한 줄에 여러 문장을 넣을 수도 있고, 한 문장을 여러 줄에 걸쳐서 할 수도 있다. 그러므로 줄과 문장을 혼동해서는 안 된다.

  1. System.out.println("첫 번째 수는 1입니다.");

  2. System.out.println("두 번째 수는 2입니다.");

  3. System.out.println("세 번째 수는 3입니다.");

위의 예제를 한 줄에 한 문장씩으로 수정하였다. 어느 쪽이 보기 좋은가?

 

선언문과 실행문을 구분하라.

  1. public static void main(String args[]) {
  2. int num1 = 10;

  3. int num2 = 20;

  4. int sum = num1 + num2;

  5. System.out.println( "첫 번째 수는 " + num1 + " 입니다." );

  6. System.out.println( "두 번째 수는 " + num2 + "입니다." );

  7. System.out.println( " 두 수의 합은 " + sum + "입니다.");

  8. }
  1. public static void main(String args[]) {
  2. int num1 = 10;

  3. int num2 = 20;

  4. int sum = num1 + num2; // 위는 선언부

  5.  

  6. System.out.println( "첫 번째 수는 " + num1 + " 입니다." );

  7. System.out.println( "두 번째 수는 " + num2 + "입니다." );

  8. System.out.println( " 두 수의 합은 " + sum + "입니다.");

  9. } // 아래는 실행부


단락을 구분하라.

  1. public static void main(String args[]) {
  2. int num1 = a + b;

  3. System.out.println( "첫 번째 수는 " + num1 + " 입니다." );

  4.  

  5. int num2 = c + b;

  6. System.out.println( "두 번째 수는 " + num2 + "입니다." );

  7.  

  8. int sum = num1 + num2;

  9. System.out.println( " 두 수의 합은 " + sum + "입니다.");

  10. }

일반 문장에서는 생각의 덩어리 하나를 표현한 여러 문장을

모아서 단락이라고 한다. 그리고 일반 문장에서는 하나의

단락을 다른 단락과 구분하기 위하여 들여쓰지만, 프로그램

코드에서는 들여쓰기 대신 빈 줄로 단락을 구분한다.


제어문들 사이를 구분하라.

제어문은 프로그램의 흐름을 바꾸는 역할을 한다. 가정에 따른 선택(while), 상황에 따른 선택(If ~ Else ~ ), 상황에 따른 반복(for)이 이 제어문에 의해서 이루어진다.

  1. public static void main(String args[]) {
  2. if ( num1 == num2) {

  3. System.out.println( "num1 과 num2는 같다“ );

  4. } else if ( num1 < num2 ) {

  5. System.out.println( "num1이 num2보다 작다“);

  6. }else {

  7. Systrem.out.println( "num1 과 num2는 다르다“);

  8. }

  9. }
  1. public static void main(String args[]) {
  2. if ( num1 == num2) {

  3. System.out.println( "num1 과 num2는 같다“ );

  4.  

  5. } else if ( num1 < num2 ) {

  6. System.out.println( "num1이 num2보다 작다“);

  7.  

  8. }else {

  9. Systrem.out.println( "num1 과 num2는 다르다“);

  10. }

  11. }


  1. switch ( selection) {
  2. case 1 :

  3. System.out.println("1을 선택하였습니다.“);

  4. break;

  5. case 2 :

  6. System.out.println("2를 선택하였습니다.“);

  7. break;

  8. default :

  9. Systrem.out.println("3을 선택하였습니다.“);

  10. }
  1. switch ( selection) {
  2. case 1 :

  3. System.out.println("1을 선택하였습니다.“);

  4. break;

  5.  

  6. case 2 :

  7. System.out.println("2를 선택하였습니다.“);

  8. break;

  9.  

  10. default :

  11. Systrem.out.println("3을 선택하였습니다.“);

  12. }


함수들 사이를 구분하여라.

함수들 사이를 띄우지 않은 경우의 단점

[1]. 함수가 어디서 시작되어 어디서 끝나는지 파악하기 어렵다.

[2]. 프로그램이 몇 개의 함수로 구성되어 있는지 파악하기 어렵다.

[3]. 특정 함수가 어디쯤 위치하고 있는지 파악하기 어렵다.

연산자의 앞뒤로 빈칸을 두라.

  1. 1. document.write("<td>"+j+"*"+i+"="+(j*i)+"</td>");

  2. // 위와 아래의 내용은 같다.

  3. 2. document.write( "<td>" + j + " * " + i + " = " + ( j * i ) + "</td>" );

  4. // 어느 쪽 코드가 읽기가 편한가?


for();

보다는

for ();

x=1;

대신에

x = 1;


단항 연산자(++, --)를 피연산자와 띄어 쓰지 마라.

  1. int p = 9;

  2. ++ p; // p는 몇일까?

f = a + ++ b - c - -- d + e; // 이 코드는 이해가 어렵다.

-> f = a + ++b - c - --d + e; // 조금 이해가 쉽다.

--> f = a + ( ++b ) - c - ( --d ) + e; // 괄호를 해주면 이해가 더 쉽다.

 

● 들여 쓸 때 좋은 코딩 습관

중괄호의 위치( {, } )

  1. public static void main ( String args[] ) {

  2. System.out.println("Hello world!!");

  3. }


보는 것처럼 중괄호는 메서드의 끝에서 열고, 닫는 괄호는 메서드의 시작 위치와 같은 선상에 위치하도록 한다.

 

중괄호의 위치를 통일시켜라.

프로그램마다 취향이 다르겠지만, 일반적으로 두 자에서 네 자를 들여 쓰는 것이 좋다.


내부 블록은 들여 써라.

블록이란 중괄호( {, } )로 둘러싸인 문장을 말한다. 일반적으로 여러 개의 문장을 다른 문장과 구분할 필요가 있을 때 블록을 만든다.


  1. public static void main ( String args[] ) {
  2. int var = 2;
  3. System.out.println("지역변수 변수 var 의 블록 밖에서의 값은 " + var );
  4. { int var = 3;
  5. System.out.println ("지역 변수 var의 블록 안에서의 값은 “ + var );
  6. }

  7. System.out.println(" 지역변수 var의 블록 밖에서의 값은 “ + var );
  8. }
  1. public static void main ( String args[] ) {
  2. int var = 2;
  3. System.out.println("지역변수 변수 var 의 블록 밖에서의 값은 " + var );
  4.  
  5. { int var = 3;
  6. System.out.println ("지역 변수 var의 블록 안에서의 값은 “ + var );
  7. }
  8. System.out.println(" 지역변수 var의 블록 밖에서의 값은 “ + var );
  9. }


피제어부는 들여써라.


  1. if ( num1 == num2 ) {

  2. Systrem.out.println("num1 과 num2는 같다“);

  3. }

  1. if ( num1 == num2 ) {

  2. Systrem.out.println("num1 과 num2는 같다“);

  3. }


= 피제어부를 들여 쓰지 않고 제어부를 완결된 문장으로 바꾼 코드(오류 발생

if ( num1 == num2 );

Systrem.out.println( "num1과 num2가 같다.“ );

들여쓰기를 점검해야 하는 시점

[1]. 프로그램을 완결하기 전
[2]. 프로그램을 다른 사람에게 배포하기 전

[3]. 파일 변환을 마친 후

 

쓸데없는 들여쓰기를 하지 마라.


  1. int Add ( int a, int b) {
  2. int result = a +b ;
  3. return result;
  4. }
  1. int Add ( int a, int b) {
  2. int result = a +b ;
  3. return result;
  4. }


중요한 것은 들여쓰기를 하는 정도를 일정하게 하는 것이다. 들여쓰기가 꼭 필요하지만 매번 들여쓸 때마다 두 칸이면 두 칸, 네 칸이면 네 칸으로 들여 쓸 공간을 동일하게 적용시켜야 혼란스럽지 않다.

 

● 식별자 이름을 지을 때 좋은 코딩 습관

 

변수 이름을 체계적으로 지어라.

너무 많은 변수는 우리를 혼란스럽게 만든다. 심지어 프로그램 작성자마저도 어지럽게 한다.

변수 이름을 짓는 방법을 통일하여 그나마 많은 변수들의 바다에 빠져 헤매지 않도록 변수의 물길을 일정하게 해두는 것이 좋다. 변수 이름을 체계적으로 정리하면 혼돈을 피할 수 있다.

클래스의 역할을 접두사로 활용하라.


  1. class getMoney {

  2. 인사잘하기;

  3. 연락 잘하기;

  4. 웃기;

  5. 알바;

  6. }

돈을 얻기라는 클래스라고 하면 돈을 뜻하는 Money와 얻기를 뜻하는 get을 합쳐서

getMoney 라는 클래스 명을 택함.

  1. document.getElementByID();

ID를 통해서 Element를 get한다.

  1. document.getElementsByName();


이름을 의미 있게 지어라.

변수 이름을 보면 누구나 그 변수에 들어가는 자료가 무엇인지 알 수 있게 하라.

int iThisYear; 이라고 하면, ThisYear가 정수값임을 알 수 있을 것이다.

 

비슷한 변수 이름을 사용하지 마라.

  1. int number;

  2. long int numbers;

  3. int number;

  4. short int num;

만약 어쩔 수 없이 비슷한 이름을 사용해야 한다면, 이름의 앞이나 뒤에 접두사 또는 접미사를 붙여서 이름을 짓도록 하자. 예를 들어, 다음과 같이 변수 이름 뒤에 숫자를 붙여서 구분하는 것은 흔한 방법이다.

  1. int number_01;

  2. int number_02;

 

의미를 잃지 않는 범위에서 짧게 지어라.

이름을 의미 있게 만들기 위해서 무작정 긴 이름으로 지으면 안된다. 예를 들면, 단지 두 개의 정수만이 필요한 프로그램에서 두 정수를 담을 변수의 이름을 다음과 같이 장황하게 지어서 선언할 필요가 없다.


  1. int first_integer_number;

  2. int ssecond_integer_number;

  3. -> int num1;

  4. int num2;

함수나 클래스의 이름도 마찬가지.

  1. New_plus_operation_function();

  2. New_minus_operation_function();

  3. -> NewPlusFunc();

  4. NewMinusFunc();

줄여서 쓸 만한 단어는 다른 사람이 이해할 수 있는 범위 안에서 무조건 줄여서 쓴다는 원칙만 잘 기억하면 된다.

 

이름이 길면 밑줄 또는 대소문자로 구분하라.

  1. Break_Control_System_Panel();

  2. BreakControlSystemPaner();

예전에는 첫 번째 방법이 많이 쓰였지만, 근래에는 두 번째 방법을 더 많이 사용한다.

 

변수 이름을 밑줄로 시작하지 마라(시작할 수 있다고 해서 사용하지 마라). 중요.


밑줄을 과도하게 사용하지 마라. 가급적이면 한번 이상 사용하지 마라.

  1. [1]. char File__name___new__name;

  2. [2]. char File___name__new__name; // 차이를 알겠는가?

대소문자를 적절히 배합해서 만들어라. 클래스에서는 첫글자는 항상 대문자로 시작하라.

  1. Breakcontrolsystempanel();

  2. BreakControlSystemPanel(); // 어느 것이 구분이 가능한가?


대소문자를 구분하여 이름을 짓는 프로그래머의 습관

1. 변수나 객체의 이름은 소문자로 시작한다.

2. 함수, 클래스, 구조형, 공용형 등의 이름은 대문자로 시작한다(메소드 포함).

3. 기호 상수나 매크로 함수는 모든 글자를 대문자로만 짓는다.


Java 가 대소문자를 구분한다는 것을 악용하지 마라.

  1. int num; // 첫 번째 입력 변수

  2. int Num; // 두 번째 입력 변수

  3. int NUM; // 세 번째 입력 변수

 

연산자의 우선 순위에 의존하는 식을 만들지 마라.

  1. a = 2 + 3 * 4; // 의 값은 얼마인가?

  2. a = ( 2 +3 ) * 4 // 의 값은 얼마인가?

 

시프트 연산 대신 산술 연산을 사용하라.

시프트 연산(shift operation, 자리 옮김 연산)에 쓰이는 << 나 >>는 빠른 배수 계산에 TM이기도 하고 비트를 오른쪽으로 한 개씩 옮기거나 왼쪽으로 자리를 옮기는 경우 크기가 1/2로 줄거나 2배로 증가되기 때문이다.

시프트 연산의 문제점 :

1. 조작하는 수치가 음수인 경우에 왼쪽 자리옮김을 하면 음수 부호가 탈락할 가능성이 있다.

2. 프로그램을 이해하기 어렵다.

극단적인 효율이 필요하지 않다면 항상 프로그램을 알기 쉽고, 이해하기 쉽게 작성해야 한다. 따라서 시프트 연산으로 수치를 조작하려는 생각은 버리자.

 

극단적으로 효율성을 추구하지 마라.

  1. num4 = ( num1 + num2 ) * num3;

다음 두 문장으로 기술된 것보다 처리 속도도 빠르고, 기억 장소도 덜 차지한다.

  1. temp = num1 + num2;

  2. num 4 = temp * num3;

극단적인 효율을 추구하는 사람은 어떻게든 처리 속도를 빠르게 하기 위해서 매우 복잡한 식을 즐겨 쓰곤 한다. 이렇게 코드를 작성하면 효율성을 얻을 수 있을지도 모르지만, 유지보수 비용이 만만치 않을 것이다. 효율성이 중요하기는 하지만, 간단하고 명료한 코드를 희생할 만큼 중요한 것은 아니다.

우리가 중점적으로 생각할 것은, 간단하고 명료해서 읽으면 이해할 수 있는 코드이다.

 

while 문에서 관계/대입 연산자의 우선순위를 혼동하지 마라.

프로그래머가 생각하는 우선순위를 맞출 수 있도록 괄호를 사용해야 한다. 하지만 프로그래머가 늘 연산자의 우선순위를 기억하고 그것을 고려하여 괄호를 넣을 수는 없다. 그렇기 때문에 while, for, if와 같은 조건식 안에서 조건 연산 외에 대입 연산 등의 다른 연산을 하지 않는 것이 좋다.

 

조건식에 대입문을 사용하지 마라.

조건식(If, for, while)은 제어문에서 아주 중요한 역할을 한다. 조건식의 결과값에 따라서 참과 거짓이 판별되기 때문이다. 프로그래머들은 조건식에 의도적으로나 또는 실수로 대입문을 사용하는 경우가 있다.

가장 흔한 실수로는 조건 식에서 등호( A == B : A와 B가 같다) 대신에 실수로 대입 연산자( A = B : B를 A에 대입한다)를 쓰는 경우다.

  1. if ( value == 0) {

  2. System.out.println("값이 0입니다.");

  3. }

실수로 다음과 같이 등호 대신 대입 연산자를 썼다면 어떻게 될까.

  1. if ( value = 0 ) { // 이 경우 value 의 데이터는 0으로 변경된다.

  2. System.out.println("값이 0입니다.");

  3. }

-> 조건식에 대입문을 의도적으로 사용한 코드

  1. while ( ( value = new_value++ / 20 ) < 10 ) {

  2. System.out.println( "value의 값은 " + value + "입니다." );

  3. }


위의 예제와 같이 조건식에 대입문을 쓸 경우에는 프로그램을 이해하기가 힘들어진다. 이런 경우에는 알기 쉬운 코드로 만들어 줘야 한다.

  1. while ( ture ) {

  2. value = new_value / 20;

  3. new_value++;

  4. if ( value >= 0 ) {

  5. break;

  6. }

  7. System.out.println"value의 값은 " + value + "입니다." );

 

부작용이 나타나지 않도록 주의하라.

  1. number = 10;

  2. result = ++number;

결과값은 얼마일까? 정답은 11이다. 그러나 정답을 10이라고 말하지 않으려면 단항 연산자의 전치(prefix)와 후치(postfix)에 대해서 이해하고 있어야 한다.


전치란 ++number처럼 단항 연산자를 피연산자 앞에 두는 방식이고, 후치란 nuber++처럼 단항 연산자를 피연산자 뒤에 두는 방식이다. 전치의 경우에는 ++의 연산이 = 연산보다 우선순위를 가져 먼저 계산되고, 후치의 경우에는 = 연산이 더 빠르게 계산되고 그후 ++이 연산된다.


  1. number = 10;

  2. result = number++; // 결과값은 얼마일까? 10이다.

  3. -> 개선

  4. number = 10;

  5. number++;

  6. result = number;

단 한줄의 문장이 늘어났을 뿐인데 앞의 코드와 같이 전치와 후치에 대해서 알아야 하거나 고민할 필요가 없다. 이런 간단한 연산에서 우리는 전치와 후치라는 부작용을 볼 수 있다. 즉, 이 연산에서 'number에 1을 더한다‘는 것은 주된 효과지만, ’++ 연산이 먼저냐, 아니면 = 연산이 먼저냐‘를 결정하는 것은 부작용인 것이다. 단항 연산자의 부작용은 식이 복잡해질수록 더 극명하게 나타난다.

부작용이 꼭 단항 연산자에만 국한되는 것은 아니다. 대입문을 포함한 거의 모든 연산식에서도 우리는 부작용을 가져올 수 있는 문장을 흔히 볼 수 있다.

  1. average = total / ( count = last_number - first_number );

이처럼 연산식 가운데에 대입문을 두게 되면, 이 대입문이 언제 어떻게 어떤 방식으로 작동하는지에 대해서 고려해야만 문장을 해독할 수 있다. 그러므로 이 문장은 간단한 형태로 바뀌어야 한다.

  1. count = last_number - first_number;

  2. average = total / count

부작용을 피하려면 프로그램을 쉽고 짧게 작성해야 한다.

 

함수의 원형에도 인수의 자료형을 표기하라.


  1. int Team(); // 인수의 자료형이 없다.
  2. void Score(); // 인수의 자료형이 모호하다.
  3. int Board(); // 모호성은 혼동을 가져온다.
  1. int Team( void );
  2. void Score( int a, char b );
  3. int Board( int[4][3], double d );


정밀한 계산이 필요하다면 부동 소수점 연산을 피하라.

부동 소수점은 특성상 정확한 연산을 할 수 없다. 다시 말하면, 늘 오차를 포함하고 있는 것이다. 예를 들어 보자. 1/3을 소수로 표현하면 0.33333333... 처럼 무한소수가 된다. 그러나 컴퓨터는 무한 소수를 표현할 수 없다.

부동 소수점 연산은 늘 오차를 포함하기 때문에,정밀한 계산을 필요로 하는 상황이라면 부동 소수점 보다는 정수를 사용해 연산을 하는 것이 좋다. 특히 금융 관련 계산을 하는 상황이라면 더욱 그렇다.

 

정밀한 계산에는 float 형보다 double 형을 사용하라.

double은 float 형에 비해 두 배 더 큰 자료형이다. 그 만큼 정밀도가 높다. 그렇기 때문에 소수 이하 자릿수를 더 늘려서 계산할 수 있는 것이다. 정밀도를 고려해서 double 형을 사용하라.

 

계산 단위를 반드시 명시하라.


  1. int num;

  2. int sum;

  3. int total;

  1. int num; // 당일 총 결산수지 : 원 단위로 계산

  2. int sum; // 한달간 총 결산수지 : 천원 단위

  3. int total; // 일년간 총 결산수지 : 백만원 단위


나눗셈 연산에는 주의를 기울여라.

  1. int num;

  2. num = 15 / 10; // num의 값은 얼마일까?

답은 1이다. 왜냐하면 15을 10으로 나누면 1.5가 되는데, 이 계산식에서 15 / 10은 정수형 자료 간의 연산이다. 이런 정수형 자료 사이의 연산에서는 소수인 0.5가 제거되고 1만이 최종 결과가 된다. 이처럼, Java 언어에서는 정수형 데이터의 나눗셈에서 가차없이 소수 이하 부분을 잘라버린다.

  1. float result;

  2. result = 1 / 3; // 의 결과값은 얼마일까?

0.33333...일 것이라 예측하겠지만, 결과는 0.0 이 나온다. 여기에 나눗셈의 함정이 숨어 있다.

result = 1 / 3; 이라는 문장에서 1/3이 먼저 정수형으로 계산되고 나서 result에 대입된다.

 

자료형의 변환이 이루어지지 않도록 하라.

강제변환 (int)(3.14f)와 같은 것도 사용을 자제하라.

 

goto 문을 사용하지 마라.

 

짜파게티 소스를 만들고 싶지 않다면, goto 문을 쓰지 마라. 나중에 후회한다.

 

조건식(조건 연산자) 보다는 if문을 사용하라.

 

  1. if ( num1 == num2 ) {

  2. temp = num 3 + f( num1 );

  3.  

  4. } else {

  5. temp = f(num1) - num3;

  6. }

이 문장을 한줄짜리 조건식으로도 바꿀 수 있음.

  1. temp = ( num1 == num2 ) ? d + f ( num1 ) : f( num1 ) - d;


조건식은 삼항 연산자를 사용하여 표현한다. 삼항 연산자란 ‘? True : False '를 말하는데, 이 연산의 대상이 되는 피연산자가 세 개라서 삼항 연산자라 불린다. 조건 연산자라고 부르기도 한다.


어느 편이 읽기 쉽고 이해하기 쉬운가? 그리고 어느 편이 더 효율적인가? 일단 효율적인 면에서 보면 한 줄 짜리 조건식이 조금 더 효율적이다. 그러나 컴퓨터 성능이 좋아진 지금에서는 큰 차이가 없다. 읽기 쉬움, 즉 가독성의 측면에서 보면 if 문이 좋다. 훈련된 프로그래머들은 조건식이 더 해석하기 쉽다고 말할지도 모르지만, 그건 소수의 훈련된 프로그래머의 얘기일 따름이다.

 

배열의 차원을 3차원으로 한정하라.

  1. multi_dimention[10][20][30][40]....[10];

배열은 얼마든지 선언해서 사용할 수 있다. 그러나 이런 배열은 무용지물이다. 우리의 사고력의 한계가 있기 때문이다.


● 오류없는 프로그램을 만들기 위한 좋은 코딩 습관

배열의 index는 0부터 시작한다는 것을 잊지마라!! 잊으면 곤란해!!


치환 문자열은 반드시 괄호를 씌워라.


괄호를 열었다면 반드시 닫아라.

이런 경우를 방지하기 위하여, 괄호를 생성시 {} () 열린 괄호와 닫힌 괄호를 바로 입력하고 그 사이에서 확장해 나가는 방식을 사용하도록 하라.

이 글은 스프링노트에서 작성되었습니다.

프로그램의 세계에서도 이미 제공되어 있는 것을 그대로 사용할 수 없을 때, 필요한 형태로 교환하고 사용하는 일이 자주 있습니다. '이미 제공되어 있는 것'과 '필요한 것' 사이에 '차이를 없애주는 디자인 패턴이 Adapter 패턴 입니다.

 

Adapter 패턴은 Wrapper 패턴으로 불리기도 합니다. wrapper는 '감싸는 것'이라는 의미를 가지며, 무엇인가를 한번 포장해서 다른 용도로 사용할 수 있게 교환해 주는 것이 wrapper 이며 adapter입니다.

 

Adapter 패턴에는 두 가지 종류가 있다.

  • 클래스에 의한 Adapter 패턴(상속을 사용한 Adapter 패턴)
  • 인스턴스에 의한 Adapter 패턴(위임을 사용한 Adapter 패턴)

 

예제 프로그램 (1) : 상속을 사용한 Adapter 패턴

지금 Banner 클래스에는 문자열을 괄호로 묶어서 표시하는 showWithParen 메소드와 문자열 전후에 *를 붙여 표시하는 showWithAster 메소드가 준비되어 있습니다. 이 Banner 클래스를 '이미 제공되어 있는 것'이라고 가정합니다.

 

한편 Print 인터페이스에서는 문자열을 느슨하게(괄호 사용) 표시하기 위한 printWeak 메소드와 문자열을 강하게 (* 표시를 앞뒤에 붙여 강조) 표시하기 위한 printString 메소드가 선언되어 있다고 가정합니다.  지금 하고 싶은 일은 Banner 클래스를 사용해서 Print 인터페이스를 충족시키는 클래스를 만드는 일입니다.

 

PrintBanner 클래스가 어댑터의 역할을 담당합니다. 이 클래스는 제공되어 있는 Banner 클래스를 상속해서, 필요로 하는 Print 인터페이스를 구현합니다. PrintBanner 클래스는 showWithParen 메소드를 사용해서 printWeak를 구현하고, showWithAster 메소드를 이용해서 printString 메소드를 구현합니다. 이것으로 PrintBanner 클래스는 어댑터의 기능을 완수하게 됩니다.

 

예제 1.  Banner 클래스(Banner.java)

  1. package Adapter;

    public class Banner {
        // Banner 클래스는 미리 제공되는 클래스라고 가정.
        private String string;
        public Banner(String string){
            this.string = string;
        }

        public void showWithParen(){
            System.out.println("(" + string + ")");
        }
       
        public void showWithAster(){
            System.out.println("*" + string + "*");
        }
    }

 

예제 2. Print 인터페이스(Print.java)

  1. package Adapter;

    public interface Print {
        public abstract void printWeak();
        public abstract void printStrong();
    }

 

예제 3. PrintBaner 클래스(PrintBanner.java)

  1. package Adapter;

    public class PrintBanner extends Banner implements Print {
       
        public PrintBanner(String string){
            super(string);
        }
       
        public void printWeak(){ // 인터페이스 Print.printWeak() 구현
            showWithParen();
        }
       
        public void printStrong(){ // 인터페이스 Print.printString() 구현
            showWithAster();
        }

    }

 

예제 4. PrintBannerMain 클래스(PrintBannerMain.java)

  1. package Adapter;

    public class PrintBannerMain {
        public static void main(String[] args){
            Print p = new PrintBanner("Hello");
            p.printWeak();
            p.printStrong();
        }
    }

 

실행결과

PrintBannerMain.JPG

 

PrintBannerMain 클래스 내에서 PrintBanner의 인스턴스를 Print 인터페이스형의 변수로 대입하는 것에 주의해야 한다. 이 PrintBannerMain는 어디까지나 Print라는 인터페이스를 사용해서 프로그래밍을 하고 있다. Banner 클래스나 showWithParen 메소드나 showWithAster 메소드는 Main 클래스의 소스 코드상에서는 완전히 감추어져 있다.

 

PrintBannerMain는 PrintBanner 클래스가 어떻게 실현되고 있는지를 모른다. 실행되는 것을 모른다는 것은 PrintBannerMain 클래스를 전혀 변경하지 않고 PrintBanner 클래스의 구현을 바꿀 수 있다는 것이다.

 

예제 프로그램 (2) : 위임을 사용한 Adapter 패턴

이전 예제 프로그램(1)은 '클래스에 의한' Adapter 패턴이었습니다. 이번에는 '인스턴스에 의한' Adapter 패턴을 살표봅시다. 예제 프로그램 (1)에서는 '상속'을 사용했지만, 이번에는 '위임'을 사용한다. (위임과 관련된 내용 링크)

 

PrintBannerMain 클래스, Banner 클래스는 예제 프로그램(1)과 동일합니다. 그러나 Print2는 인터페이스가 아니고 클래스라고 가정합니다. 즉, Banner 클래스를 이용해서 Print2 클래스와 동일한 메소드를 갖는 클래스를 실현하려는 것입니다. Java 에서는 2 개의 클래스를 동시에 상속할 수 없기 때문에(단일 상속), PrintBanner2 클래스를 Print와 Banner 모두의 하위 클래스로 정의할 수 없습니다.

 

PrintBanner2 클래스는 banner 필드에서 Banner 클래스의 인스턴스를 가집니다. 이 인스턴스는 PrintBanner2 클래스의 생성자에서 생성합니다. 그리고 printWeak 및 printStrong 메소드에서는 banner 필드를 매개로 showWithParen, showWithAster 메소드를 호출합니다.

 

이전 예에서는 자신의 상위 클래스에서 상속한 showWithParen, showWithAster 메소드를 호출하고 있지만, 이번에는 필드를 경유해서 호출하고 있습니다.

 

PrintBanner2 클래스의 printWeak 메소드가 호출되었을 떄, 자신이 처리하는 것이 아니라 별도의 인스턴스인 showWithParen 메소드에게 위임하고 있습니다.

 

예제 4. Print2 클래스(Print2.java)

  1. package Adapter;

    public abstract class Print2 {
        public abstract void printWeak();
        public abstract void printStrong();
    }

 

예제 5. PrintBanner2 클래스(PrintBanner2.java)

  1. package Adapter;

    public class PrintBanner2 extends Print2 {
        private Banner banner; // Banner 를 위임하여 받음
       
        public PrintBanner2(String string){
            this.banner = new Banner(string);
        }

        public void printWeak(){
            banner.showWithParen();
        }
       
        public void printStrong(){
            banner.showWithAster();
        }
    }

 

예제 6. PrintBannerMain2 클래스(PrintBannerMain2.java)

  1. package Adapter;

    public class PrintBannerMain2 {
        public static void main(String[] args){
            Print2 p = new PrintBanner2("Hello");
            p.printWeak();
            p.printStrong();
        }
    }

 

Adapter 패턴의 등장인물

  • Target(대상)의 역할
    지금 필요한 메소드를 결정합니다. 노트북을 작동시키기 위한 직류 12볼트에 해당합니다. 예제 프로그램에서는 Print 인터페이스(상속)나 Print 클래스(위임)가 이 역할을 합니다.

 

  • Client(의뢰자)의 역할
    Target 역할의 메소드를 사용해서 일을 합니다. 직류 12볼트로 움직이는 노트북에 해당합니다. 예제 프로그램에서는 PrintBannerMain 클래스가 이 역할을 수행합니다.

 

  • Adaptee(개조되는 쪽)의 역할
    Adapt-er(개조하는 쪽)가 아니고 Adapt-ee(개조되는 쪽)입니다. Adaptee는 이미 준비되어 있는 메소드를 가지고 있는 역할입니다. 교류 100볼트의 AC 전원에 해당되며, 예제 프로그램에서는 Banner 클래스가 이 역할을 합니다. 이 Adaptee역의 메소드가 Target 역할의 메소드와 일치하면(노트북 사용 전압과 가정에 공급되는 전압이 같다면 어답터가 필요 없음) 다음 Adapter의 역할은 필요없습니다.

 

  • Adapter 의 역할
    Adapter 패턴의 주인공이니다. Adaptee 역할의 메소드를 사용해서 어떻게든 Target 역할을 만족시키기 위한 것이 Adapter 패턴의 목적이며, Adapter 역할의 임무입니다. 예제 프로그램에서는 PrintBanner 클래스가 Adapter의 역할을 합니다.

    클래스에 의한 Adapter 패턴의 경우에는 Adapter의 역할은 '상속'을 사용한 Adaptee의 역할을 이용하지만, 인스턴스에 의한 Adapter 패턴의 경우에는 '위임'을 사용한 Adaptee의 역할을 이용합니다.

 

버전 업과 호환성

소프트웨어는 버전 업이 필요합니다. 소프트웨어를 버전 업할 때는 '구 버전과 호환성'이 문제가 됩니다. 구 버전을 버리면 소프트웨어의 유지와 보수는 편하지만 항상 그것이 가능할 순 없습니다. 구 버전을 버리면 소프트웨어의 유지와 보수는 편하지만 항상 그것이 가능할 순 없습니다. 구 버전과 신 버전을 공존시키고, 유지와 보수도 편하게 하기 위해서 Adapter 패턴이 도움이 되는 경우가 있습니다. 예를 들어 앞으로는 신 버전만 유지와 보수를 하고 싶을 때 신 버전을 Adaptee 역할로 하고, 구 버전을 Target 역할로 합니다. 그리고 신 버전의 클래스를 사용해서 구 버전의 메소드를 구현하는 Adpater 역할의 클래스를 만듭니다. 

 

관련 패턴

  • Bridge 패턴
    Adapter 패턴은 인터페이스(API)가 서로 다른 클래스를 연결하는 패턴입니다. Bridge 패턴은 기능의 계층과 구현의 계층을 연결시키는 패턴입니다.
  • Decorator 패턴
    Adapter 패턴은 인터페이스(API)의 차이를 조정하기 위한 패턴입니다. Decorator 패턴은 인터페이스(API)를 수정하지 않고 기능을 추가하는 패턴입니다.

이 글은 스프링노트에서 작성되었습니다.

현재 CJ기술교육센터에서 JAVA EXPERT 과정에서 2개월의 교육과정으로 들어서고 있습니다.
첫 2주에는 HTML/CSS/JAVASCRIPT에 대해서 배우고, 또 3주간 Java Fundmental을 하고, 또 3주간 Oracle 과정을 공부하였습니다. 지금은 JBCD->JSP로 넘어갈 준비를 하고 있습니다.

이 과정에서 허니몬이 절실히 느낀 것은 자신이 공부한 것들, 혹은 경험한 것들에 대해서 기록을 남기는 것 입니다. 공부하거나 경험하는 그 순간에야, '필요하면 다 기억나겠지~' 하는 생각으로 지나치지만, 막상 시간이 지나고 나면 기억이 나지 않습니다.

지금 교육 과정을 진행하면서 꾸준하게 나만의 위키로 스프링노트(http://sunfuture.springnote.com)를 작성하고 있습니다. 이제 서서히 습관이 들어가고 있는 듯 합니다. 스프링노트의 장점이라면, 작성해둔 문서들을, 검색을 통해서 쉽게 찾을 수 있다는 겁니다. 배웠던 것들에 대해서 문득 떠오르는 단어나 키워드만으로도 다시 찾아볼 수 있는 것이죠.


더불어서 그림에서 볼 수 있는 것처럼, 제가 작성했거나 수정한 페이지들도 보여지는 기능을 보여줍니다.

결론을 말씀 드리자면, 자신이 편하게 사용할 수 있는 도구를 이용하여 자신이 공부한 내용을 기록하는 습관을 가지자 라는 것입니다. 사람의 기억력은 우리의 상상 이상으로 그리 오래 가지 못합니다. 기억력을 오래 유지하는 방법은 꾸준하게 자주 배운 것들을 열어보는 습관을 가지는 것입니다. ^^

+ Recent posts