프로그래밍 – 구현
설계작업들이 끝나면 시작되는 소프트웨어 프로그래밍은 요구사항에 대한 실질적인 구현행위이다. 특히 분리하여 구현할 수 있는 작은 단위로 나눠 작업을 한다. 프로그래밍의 수행이 상세 설계나 사용자 지침에 기술된 내용과 일치되도록 작업하여야 한다. 이 작업에서 가장 중요한 것은 표준을 정하고 준수하고 이에 따라 정확하게 작성하는 것이다. 소프트웨어의 품질은 결국 이를 얼마나 잘 수행하여 원시 코드에 반영하느냐이다. 따라서 프로그래밍에서는 소프트웨어의 기능구현이 급급한 것이 아니라 요구되는 품질에 얼마나 부합하도록 작업하는가에 대한 관심과 끊임없는 노력이 매우 중요하다.
※ 코딩과 프로그래밍?
이 용어는 모두 컴퓨터와 상호 작용하고 컴퓨터 시스템을 생성하고 유지 관리하는 데 중요한 기술로 종종 같은 의미로 사용되지만, 프로그래밍 과정 중 코드를 작성하는 구체적인 단계를 가리키며, 프로그래밍은 문제 해결을 위한 전체적인 과정을 의미하기에 사실은 서로 다르다.
- 코딩(coding): 프로그래밍의 하위 세트 중 하나로 구체적으로 언어의 문법을 따라 컴퓨터가 이해할 수 있는 명령어를 작성(원시 코드)하는 작업으로, 프로그램 로직을 작성하고 알고리즘을 구현하며 문제를 해결하기 위한 코드를 작성하는 활동
- 프로그래밍(programming): 문제 해결을 위해 컴퓨터에 명령을 전달하는 전체적인 과정으로 문제를 이해, 정의하고 요구사항을 분석하며 그에 따라 적절한 알고리즘과 데이터 구조를 선택한 후 코드를 작성하는 단계와 함께 디버깅, 테스트, 최적화 등의 소프트웨어 개발 과정을 포괄하는 개념으로 컴퓨터 시스템을 생성하고 유지관리하는 프로세스
프로그래밍 원리
앞서 용어 정의와 같이 코딩은 프로그래밍 작업이다. 이러한 구현단계의 목표는 설계 명세에 나타난 대로 사용자 요구를 만족할 수 있도록 프로그래밍을 하는 것이다. 이렇게 하기 위해서는 코딩 단계에서는 전 단계의 문서들을 잘 참조하여야 한다. 특히 무엇보다도 오류가 적은 품질 좋은 프로그램을 작성하는데 그 목표가 있다. 신속성도 중요하지만 견고한 코드를 만들어내는 것에는 많은 연습과 경험이 필요하다. 이를 위하여 수많은 원리와 가이드를 만들고 잘 참고해야 한다.
일반적인 객체지향 코딩에는 다음과 같은 단계가 있다.
- 원시 코드 스타일의 코딩표준 작성
- 아키텍처 설계 결과 프레임워크 패키지와 응용 패키지 결정
- 패키지 내 각 클래스에 대해 요구사항과 상세설계를 반영한 메소드 코딩
- 클래스 구현 후 인스펙션 실시
- 클래스 단위 테스트 진행
- 클래스/패키지를 배포, 통합
이러한 작업을 진행하면서 오류는 프로그래머가 가장 신경 써야 할 부분 중 하나다. 개발과정에서는 특히 많은 시간이 오류를 찾고 해결하는 데 쓰이는데 일정 부분 이상은 흔히 발견되는 오류로 이를 알고 접근한다면 많은 부분의 비용을 절감할 수 있다. 아래는 여러 오류 중 중요한 몇 가지의 예이다.
- 메모리 누수: 이는 메모리가 풀리지 않고 계속 할당하는 현상으로 보통 가비지 컬렉션이 자동으로 되지 않은 언어에서 이러한 오류가 자주 발생한다. 메모리 누수는 규모가 작은 프로그램에서는 영향이 적지만 대규모 환경에서는 시스템에 매우 치명적인 영향을 줄 수 있으므로 반드시 잡아야 한다.
- 중복된 해제 선언: 프로그램 내에서는 자원할당 후 사용 후 해제해야 한다. 그런데 해제된 자원을 또다시 해제하는 경우도 매우 심각할 수 있다. 만약 두 개의 해제문장 사이에 메모리 할당 호출이 있다면 이런 오류의 영향은 예측할 수 없다.
- NULL: null을 포인트하고 있는 곳의 내용에 접근하려고 하면 오류가 난다. 이는 시스템을 다운시키기에 충분하다. 그런데 찾기도 어려운 것이 이것이다.
- 배열 인덱스 오류: 배열 인덱스의 한도를 벗어나면 예외 오류가 발생한다. 그러므로 인덱스가 음수 또는 한도를 벗어나지 않는지 항시 확인하여야 한다.
- 수식 예외: 0으로 나눈다거나 변동 소수점 예외 오류 등이다.
- 사용자 정의 자료형: 오버플로우나 언더플로우가 특히 많다.
- 버퍼 오류: 보안결함에서 많이 발견되는 유형으로 해커들의 잠입 루트가 되기도 한다.
- 동기화: 공통된 자원에 접근하는 다수의 스레드가 있는 병렬 프로그램에서 흔하게 발견된다. 이 또한 쉽게 발견되기가 어려운데 시스템에는 막대한 지장을 줄 수 있다. 흔히 deadlock이 여기에 속한다.
구조적 프로그래밍, 이를 위해서 많이 사용하는 제어구조가 있는데 순차, 선택, 반복이다. 이 또한 제대로 된 흐름제어 기준을 정해놔야 한다. 무조건적인 제어의 흐름을 막고 알고리즘을 명확하게 구현할 수 있는 가이드를 제시하고 공유한다. 물론 근래의 프로그래밍 언어들은 이를 고려하여 여러 명령어나 제약을 두고 있으며 필요하다면 비구조적 문형으로 개발을 할 수도 있다.
또한 모든 프로그램에는 항상 도메인에 관한 정보를 다루기 때문에 자료구조가 항시 존재한다. 그래서 특정 정보에 대해서는 정해진 오퍼레이션만 적용된다. 즉 문제 도메인에 있는 아주 작은 정보가 제한된 방법으로만 사용된다는 것이다. 이를 앞서 다뤄본 정보은닉의 원리 적용이다. 정보은닉이란 모듈 사이의 결합을 줄이고 시스템의 유지보수를 쉽게 만드는 장점을 가지고 있고 데이터를 관리하려는 관점과 데이터를 사용하는 관점을 분리할 수 있는 것이다.
코딩 스타일
프로그래밍 스타일이다. 같은 작업을 위하여 여러 사람이 작성한 프로그램들은 문장의 패턴이나 그 구성 등 여러 면에서 다른 스타일을 보인다. 각자의 개성이다. 하지만 이를 방치할 경우 프로젝트는 원하는 목표를 달성하기 어렵다. 그래서 가르치고 학습할 수 있는 공통된 스타일을 만들어야 한다. 물론 여기에서도 기준을 구하기 어려울 수도 있지만 가장 근본은 간결하고 읽기 쉬워야 한다는 것이다. 이를 기준 삼아 코딩 스타일을 만든다.
- 명명 규칙 예
- 패키지: 타 사용자들을 위해 패키지 이름 앞에 도메인명을 붙인다. 이를 통해 패키지의 용도와 목적을 잘 나타낼 수 있다.
- 타입: 클래스(명사)와 인터페이스(명사, 형용사) 등의 명칭 첫 글자를 대문자로 한다. 이는 일반 변수와 구별할 수 있다.
- 메소드: 타입과 달리 첫 글자를 소문자(동사)로 하고 연속되는 단어의 첫 글자는 대문자로 한다. 이는 메소드 호출과 생성자 호출과 구별할 수 있게 해준다.
- 변수: 메소드와 같은 형식이다.
- 상수: 보통 대문자로 구성하며 단어 사이는 밑줄로 연결한다.
- 포인터와 레퍼런스: 포인터를 매개변수로 사용하지 않고 레퍼런스 타입으로 한다.
- 자료형: 오브젝트 타임을 돌려보내는 클래스는 특정 타입의 객체를 배출하도록 캐스트 한다.
- 문장과 수식: 반복 문장이나 수식은 메소드나 클래스로 패키지화하여 사용한다.
- 오류처리: 오류 데이터 타입 제한, 입력 처리 전 데이터 소스와 인터페이스 하여 사전 확인 등의 방법이 필요하다.
- 들여쓰기: 코드 블록의 계층 구조를 명확하게 표현할 수 있게 공백 문자나 탭을 사용한다.
- 주석: 코드 설명을 부가적으로 추가한다. 양의 문제보다는 내용을 정확히 전달하는 것이 중요하다.
- 코드 문서화: 다른 사람들이 정확하게 사용할 수 있도록 주석을 명확히 하고 문서화도 병행한다.
- 포맷 도구 사용: 개발 환경 편집기에 있는 기능으로 스타일가이드를 바탕으로 세팅하여 사용할 수 있다. 이를 통해 일관된 스타일 유지가 가능하다.