2. 목차
Coding Convention
• 코딩 컨벤션이란?
• 코딩 컨벤션의 요소
- 주석, 네이밍, 코드 작성 규칙
• 언어 별 코딩 컨벤션
- C/C++, Java, C#, Javascript …
• 코딩 컨벤션에 도움이 되는 IDE 확장 툴
- Visual Studio
3. 목차
Commenting Guide / Tools
• 주석(Comment)의 필요성
• 잘 짜여진 코드는 문서가 필요 없다?
• 주석을 작성하는 대표적인 방법들(C#)
- 소스 파일, 클래스, 메소드 …
• 언어 별 코멘팅 가이드
- C/C++, Java, Javascript …
• 코멘팅에 도움이 되는 IDE 확장 툴
• 코드 문서화 도구
5. 코딩 컨벤션이란?
• 코드를 작성하는 여러 규칙
• 들여쓰기(Indentation) 방법 → Tab 또는 Space(2, 4) ?
• 라인 당 최대 가이드 라인의 기준(80, 100)
• 중괄호(Brace) 사용 기준
• Namespace, Class, Method, Variable … 등의 명명 규칙
• 소스 코드 파일 이름 작성법
• 주석 작성 규칙
7. 코딩 컨벤션이 필요한 이유
• 코드를 최초로 작성한 사람이 끝까지 유지 보수를 한다는 보장은 없다.
*.cs / *.cpp / *.java
A
B
C
who?
8. 코딩 컨벤션이 필요한 이유
• 일관된 규칙 없이 작성된 코드는 같은 프로젝트 멤버라도 빠른 시간 안에
파악하기 힘듬. 이는 생산성과 업무 효율에 영향을 미칠 수 있다.
PdfEngine.cs
BaseEngine.cs
XpsEngine.cs
……………………………….
……………………………….
……………………………….
……………………………….
……………………………….
……………………………….
……………………………….
9. 코딩 컨벤션이 필요한 이유
• 컨벤션 내에서 작성된 코드는 개발자가 다른 부분을 신경 쓸 필요 없이
비즈니스 구현에 좀 더 집중 할 수 있게끔 도와 준다.
• 내가 작성한 코드는 아니지만 파악하기가 수월하다. (가독성 ↑ ↑ )
• 파악하기가 수월해지므로 유지 보수의 비용이 절감된다.
• 코드의 품질이 높아진다.
11. 코딩 컨벤션이 잘 적용된 예제
Chromium Project
- 구글의 웹브라우저 오픈소스 프로젝트
- 프로젝트 커미터의 수 : 22명 (공식 리스트 기준. 더 있을지도…?)
- 크롬은 크로미움을 바탕으로 개발되는 Google Product.
PDF
Flash Plugin
AV Codec
Other Libraries …
13. 코딩 컨벤션이 잘 적용된 예제
#include "base/guid.h"
#include "base/rand_util.h"
#include "base/stringprintf.h"
namespace base {
bool IsValidGUID(const std::string& guid) {
const size_t kGUIDLength = 36U;
if (guid.length() != kGUIDLength)
return false;
std::string hexchars = "0123456789ABCDEF";
for (uint32 i = 0; i < guid.length(); ++i) {
char current = guid[i];
if (i == 8 || i == 13 || i == 18 || i == 23) {
if (current != ‘-')
return false;
} else {
if (hexchars.find(current) == std::string::npos)
return false;
}
}
return true;
}
} // namespace base
• Tab 대신 Space를 2칸씩 사용한다.
• 다른 header 파일을 참조 할 경우,
root부터 header 파일까지의 경로를 모두 적는다.
• namespace 내부 선언은 들여쓰기를 하지 않는다.
• namespace가 끝나는 부분은
// namespace “name” 을 명시한다.
• Function은 대문자로 시작하며 언더바를
사용하지 않는다.
• 상수는 k로 시작한다. (kGUIDLength)
• 가이드 라인은 최대 80 컬럼으로 제한한다.
14. #include "base/task_runner.h“
#include "base/compiler_specific.h“
#include "base/logging.h“
#include "base/threading/post_task_and_reply_impl.h"
namespace base {
namespace {
// TODO(akalin): There's only one other implementation of
// PostTaskAndReplyImpl in WorkerPool. Investigate whether it'll be
// possible to merge the two.
class PostTaskAndReplyTaskRunner : public internal::PostTaskAndReplyImpl
{
public:
explicit PostTaskAndReplyTaskRunner(TaskRunner* destination);
private:
virtual bool PostTask(const tracked_objects::Location& from_here,
const Closure& task) OVERRIDE;
// Non-owning.
TaskRunner* destination_;
};
PostTaskAndReplyTaskRunner::PostTaskAndReplyTaskRunner(
TaskRunner* destination) : destination_(destination) {
DCHECK(destination_);
}
bool PostTaskAndReplyTaskRunner::PostTask(
const tracked_objects::Location& from_here,
const Closure& task) {
return destination_->PostTask(from_here, task);
}
} // namespace
} // namespace base
#include "base/sys_info.h“
#include <windows.h>
#include "base/files/file_path.h“
#include "base/logging.h“
#include "base/memory/scoped_ptr.h“
#include "base/stringprintf.h“
#include "base/threading/thread_restrictions.h“
#include "base/win/windows_version.h“
namespace {
int64 AmountOfMemory(DWORDLONG MEMORYSTATUSEX::* memory_field) {
MEMORYSTATUSEX memory_info;
memory_info.dwLength = sizeof(memory_info);
if (!GlobalMemoryStatusEx(&memory_info)) {
NOTREACHED();
return 0;
}
int64 rv = static_cast<int64>(memory_info.*memory_field);
if (rv < 0)
rv = kint64max;
return rv;
}
} // namespace
16. 코딩 컨벤션의 구성 요소
들여쓰기
(Indentation)
네이밍 규칙
클래스, 메소드,
변수
(static, member,
local)
중괄호
(Brace)
주석
(Comment)
소스 코드
파일 분류 및
네이밍 규칙
17. 코딩 컨벤션의 구성 요소
• 들여쓰기(Indentation)
- 코드의 가독성을 높이기 위해 왼쪽 첫번째 컬럼부터 오른쪽으로 일정한 간격을
띄워 쓰는 방법
- Tab 또는 Space를 사용하는데, 일반적으로 Space 사용을 권장.
Tab은 에디터마다 간격이 다르기 때문에 대부분의 에디터에서 동일하게 표현하고
싶다면 Space를 사용해야 함. (일반적으론 4칸을 사용)
- 최신 IDE는 대부분 Tab → Space으로 자동변환 기능을 제공.
18. 코딩 컨벤션의 구성 요소
• 들여쓰기(Indentation)
- 매개변수가 많은 메소드일 경우 아래와 같이 좌측 정렬을 권장.
public static void AddRecipientsToOutlook(
Outlook.MailItem mailItem,
Outlook.Recipients recipients,
Outlook.OlMailRecipientType type,
bool clear = true)
{
// …
}
19. 코딩 컨벤션의 구성 요소
• 들여쓰기 스타일의 종류 [link]
static char*
concact (char *s1, char *s2)
{
while (true)
{
something();
}
finalthing();
}
GNU 스타일
static char*
concact (char *s1, char *s2)
{
while (true) {
something();
}
finalthing();
}
K & R 스타일
static char*
concact (char *s1, char *s2)
{
while (true)
{
something();
}
finalthing();
}
BSD 스타일
20. 코딩 컨벤션의 구성 요소
• 네이밍 규칙(Naming Convention)
- 클래스, 함수, 변수 등의 이름을 작성하는 규칙으로, 이해하기 쉬운 이름으로 작성해야
개발자가 소스 코드 구조를 쉽게 알 수 있다.
- 이름을 축약하기 보단 길어도 좋으니 명확하게 작성하는 것이 좋다.
다만, 아래와 같이 너무 길면 곤란할지도...
Java – com.java.swing.plaf.nimbus package
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
Objective C – NSBitmapImageRep
- (id)initWithBitmapDataPlanes:(unsigned char **)planes
pixelsWide:(NSInteger)width pixelsHigh:(NSInteger)height
bitsPerSample:(NSInteger)bps samplesPerPixel:(NSInteger)spp
hasAlpha:(BOOL)alpha isPlanar:(BOOL)isPlanar
colorSpaceName:(NSString *)colorSpaceName
bitmapFormat:(NSBitmapFormat)bitmapFormat
bytesPerRow:(NSInteger)rowBytes
bitsPerPixel:(NSInteger)pixelBits;
21. 코딩 컨벤션의 구성 요소
• 네이밍 규칙(Naming Convention)
- 네이밍 규칙은 일반적으로 선립되어 있는 규칙들이 많이 사용 됨.
특정 언어(+ 컴파일러) 개발사가 가이드를 제시하는 경우도 많음.
분류 특징(예제) 사용되는 언어 및 플랫폼
Pascal Case BackColor, IsConnected C++(VC), .NET(c, m, p), Java(c)
Camel Case backColor, isConnected Java(m), Javascript(f)
Upper Case System.IO .NET
Snake Case hello_world(), get_name() C/C++, Ruby
Hungarian notation szName, arrNumberList, bReadLine, g_nWheel C/C++(VC, MFC)
22. @EReceiver
public class PackageBroadcastReceiverServiceImpl extends AbstractBroadcastReceiver
implements Service {
private final Logger logger = Logger.getLogger(PackageBroadcastReceiver.class);
@Pref
protected Preferences mPrefs;
@Override
public void onReceive(Context ctx, Intent it) {
String action = it.getAction();
if (action == null)
return;
switch (action) {
case Intent.ACTION_PACKAGE_ADDED: {
// …
}
break;
case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
// …
}
break;
}
}
@Override
protected void onServiceConnected(Context ctx) {
// …
}
}
일반적인 Java 코드
• Class는 Pascal Case를 사용.
Interface를 구현했다면
“클래스명 + 인터페이스명 + Impl”.
• 추상 클래스는 클래스 앞에
Abstract를 표기.
• 상수는 대문자 언더바(‘_’)를 사용.
• 멤버 변수는 “mName“ 또는 “m_Name”
, “_name” 등으로 prefix를 붙여
로컬 변수와 구분이 가능하게 작성.
• 메소드는 Camel Case를 사용
• 조건문(if)의 내부 코드가 1줄로 끝나면
중괄호(Brace)를 사용하지 않음.
• Indentation은 2 or 4칸
23. 일반적인 C# 코드
• Class는 Pascal Case를 사용.
추상 클래스도 동일하게 표기.
• 인터페이스의 prefix는 I를 사용.
• 멤버 변수의 경우 동일한 프로퍼티가
있다면 Camel Case로 작성. 또는
언더바(_) prefix를 붙이기도 함.
• 메소드는 Pascal Case를 사용.
• 조건문(if)의 내부 코드의 길이와 상관
없이 Brace로 블록 처리.
• Java 진영과 달리, Brace는 항상
new line으로 시작.
• Indentation은 대부분 4칸을 사용.
namespace Test
{
public abstract class BaseClass
{
public abstract string Name { get; set; }
}
public class DerivedClass : BaseClass, IDisposable
{
private bool disposed = false;
private string name;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
//
}
disposed = true;
}
public override string Name
{
get { return name; }
set { name = value; }
}
}
}
24. 코딩 컨벤션의 구성 요소
• 소스 코드 파일 분류 및 네이밍 규칙
- “디렉토리/소스파일” 구조를 권장. 디렉토리 구분은 코드의 기능 & 성격별로 묶는다.
- .NET에선 디렉토리가 namespace 최소 단위를, Java에선 package를 의미한다.
- C/C++은 디렉토리와 매칭되는 단위 개념은 없으나, .NET, Java와 같이 구성하는 것은
어렵지 않으므로 동일한 방식을 권장함.
- 소스 파일명은 .NET, Java의 경우 Pascal Case를, C/C++은 Snake/Pascal Case를 사용.
39. /// @brief 파라메터에 해당하는 캐시가 존재하는지 검사한다.
/// @param dm : 디스플레이 모델
/// @param pageNo : 페이지 번호
/// @param rotation : 회전 각도
/// @param zoom : 배율
/// @param pTile : 타일
/// @return true : 타일에 해당하는 캐쉬가 존재.
/// @return false : 존재하지 않음.
bool Renderer::ExistsCache(const DisplayModel &dm, int pageNo, int rotation, float zoom, const TilePosition *pTile) {
auto pCache = FindCache(dm, pageNo, rotation, zoom, pTile);
if (pCache)
DropCache(pCache);
return pCache != nullptr;
}
/// @brief 캐시 리스트에서 해당 캐시를 삭제한다.
/// @param cache : 캐시 리스트에서 삭제 할 캐시.
void Renderer::DropCache(BitmapCacheEntry *pCache) {
ScopedCritSec scope(&m_CacheAccess);
assert(pCache);
if (!pCache)
return;
if (0 == --pCache->refs) {
PDF_LOG(INFO) << "Drop the cache. [Page No : " << pCache->page_no << "]";
delete pCache;
}
}
/// @brief 캐시된 페이지를 해제한다.
/// @param pDm : DisplayModel
/// @param pageNo : 페이지 번호
/// @param pTile : Tile Position
void Renderer::FreeCachedPage(const DisplayModel *pDm, int pageNo, const TilePosition *pTile) {
- 의미 없는 많은 주석은 오히려 전체 코드에
대한 가독성을 떨어뜨릴 수 있다.
- 함수/매개변수명만 잘 지어도 대부분의 주석은
생략이 가능하다. (이런 경우 구현 로직에 대한
간단한 설명만 붙여도 좋다. SI에선 불가능..)
- 좌측처럼 함수, 매개변수, 리턴값에 대한
주석들은 기능 수정시 오히려 관리 포인트만
가중시키는 결과를 낳는다. (중요한 부분이
아닌데도..!)
- 주석 다느라 시간 다 보낸다. (개발은 언제?)
- 하지만 특정 Platform을 개발하고 API를
제공 할 계획이라면, 함수 단위 주석은 필수다.
42. #include <stack>
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/synchronization/lock.h"
namespace base {
// This class provides a facility similar to the CRT atexit(), except that
// we control when the callbacks are executed. Under Windows for a DLL they
// happen at a really bad time and under the loader lock. This facility is
// mostly used by base::Singleton.
//
// The usage is simple. Early in the main() or WinMain() scope create an
// AtExitManager object on the stack:
// int main(...) {
// base::AtExitManager exit_manager;
//
// }
// When the exit_manager object goes out of scope, all the registered
// callbacks and singleton destructors will be called.
class BASE_EXPORT AtExitManager {
...
};
} // namespace base
at_exit.h
• Class에 대한 설명, 활용도 및
사용 방법에 대한 안내 제공.
• AtExitManager는 Application 종료시
Manager에 등록된 콜백 또는 Task를
호출하여 마무리 작업을 할 수 있게
끔 도와줌.
43. class BASE_EXPORT AtExitManager {
public:
typedef void (*AtExitCallbackType)(void*);
AtExitManager();
// The dtor calls all the registered callbacks. Do not try to register more
// callbacks after this point.
~AtExitManager();
// Registers the specified function to be called at exit. The prototype of
// the callback function is void func(void*).
static void RegisterCallback(AtExitCallbackType func, void* param);
// Registers the specified task to be called at exit.
static void RegisterTask(base::Closure task);
// Calls the functions registered with RegisterCallback in LIFO order. It
// is possible to register new callbacks after calling this function.
static void ProcessCallbacksNow();
protected:
// This constructor will allow this instance of AtExitManager to be created
// even if one already exists. This should only be used for testing!
// AtExitManagers are kept on a global stack, and it will be removed during
// destruction. This allows you to shadow another AtExitManager.
explicit AtExitManager(bool shadow);
private:
base::Lock lock_;
std::stack<base::Closure> stack_;
AtExitManager* next_manager_;
// Stack of managers to allow shadowing.
DISALLOW_COPY_AND_ASSIGN(AtExitManager);
};
• 함수에 대한 설명 및 주의 사항을
제공.
• 함수/매개변수에 대한 교과서적인
설명보단 구현 원리, 로직의 흐름에
초점을 맞춘 주석을 제공.
44. • at_exit.h 에서 대부분의 설명이 제공
되기 때문에 소스 파일에선
특별히 주석을 제공하지 않는다.
• 또한 클래스와 함수명이 직관적이기
때문에 눈으로도 쉽게 이해 할 수 있다.
• 잘 작성된 코드는 그 자체가 문서와
같다.
AtExitManager::AtExitManager() : next_manager_(g_top_manager) {
// If multiple modules instantiate AtExitManagers they'll end up living in this
// module... they have to coexist.
#if !defined(COMPONENT_BUILD)
DCHECK(!g_top_manager);
#endif
g_top_manager = this;
}
AtExitManager::~AtExitManager() {
if (!g_top_manager) {
NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
return;
}
DCHECK_EQ(this, g_top_manager);
ProcessCallbacksNow();
g_top_manager = next_manager_;
}
...
void AtExitManager::RegisterTask(base::Closure task) {
if (!g_top_manager) {
NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
return;
}
AutoLock lock(g_top_manager->lock_);
g_top_manager->stack_.push(task);
}
void AtExitManager::ProcessCallbacksNow() {
if (!g_top_manager) {
NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager";
return;
}
AutoLock lock(g_top_manager->lock_);
while (!g_top_manager->stack_.empty()) {
base::Closure task = g_top_manager->stack_.top();
task.Run();
g_top_manager->stack_.pop();
}
}
at_exit.cc
46. Sandcastle Tool (.NET CHM Generator)
• MS에서 제공하는 MSDN 스타일
코드 문서화 툴
• 빌드된 DLL들을 대상으로
Reflection을 통해 문서를 생성.
• 프로젝트 빌드시 생성되는 XML
documentation file과 통합 가능.
• 문서 생성시 시간이 꽤 걸린다는
단점이 있음.
(.NET DLL을 포함 시킨 경우에만…)
• 참고 [link]
47. Sandcastle Tool (.NET CHM Generator)
• 주석은 좌측과 같이 작성.
• 코드 상단위에서 /// 를 입력하면
Summary가 자동 완성 됨.
namespace WindowsFormsApplication1
{
/// <summary>
/// 테스트 폼
/// </summary>
public partial class Form1 : Form
{
/// <summary>
/// 생성자
/// </summary>
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 윈도우 캡션을 세팅한다.
/// </summary>
/// <param name="title">캡션 타이틀</param>
public void SetWindowText(string title)
{
Text = title;
}
}
}
48. Sandcastle Tool (CHM Generator)
1. Sandcastle tool 설치 [link]
2. Sandcastle Help File Builder 설치 [link]
3. C:Program Files (x86)EWSoftwareSandcastle Help File Builder
SandcastleBuilderGUI.exe 실행
4. File -> New Project -> 프로젝트 이름으로 저장
5. Project Exploerer에서 Documentation Sources에 문서를 생성 할
DLL과 Xml documentation file을 추가
6. Project Properties -> Help File에 CHM 문서에 대한 정보를 기입
7. “Ctrl + Shift + B”를 눌러 빌드를 시작.
55. Doxygen Wizard (C/C++, Java, C#, PHP CHM Generator)
• Java/C++(GNU) 진영에서 많이
사용하는 코드 문서화 툴.
• 코드 상단에 Doxygen format으로
작성된 주석을 파싱하여 생성.
• VS의 Code Tooltip 포맷으로
사용되는 Summary 포맷과 달라서
VS와의 궁합은 좋지 않음.
• 여러 언어를 지원하기 때문에,
툴 또한 Cross Platform을 지원.
• CHM, HTML, PDF 등의 포맷 지원.
56. Doxygen Wizard (C/C++, Java, C#, PHP CHM Generator)
• 주석은 좌측과 같이 작성.
• Doxygen Add-In을 설치했다면 상단 툴바에
“Add Code Comment”라는 버튼이 추가됨.
주석을 작성하고자 하는 코드에 커서를 두고
버튼을 누르면 포맷이 추가 됨.
namespace WindowsFormsApplication1
{
/*!
* brief 테스트 폼
*/
public partial class Form1 : Form
{
/*!
* brief 생성자
*/
public Form1()
{
InitializeComponent();
}
/*!
* brief 윈도우 캡션을 세팅한다.
* param title 타이틀 캡션
*/
public void SetWindowText(string title)
{
Text = title;
}
}
}
57. Doxygen Wizard (C/C++, Java, C#, PHP CHM Generator)
1. Doxygen Wizard 설치 [link]
2. DoxyComment Add-In for VS 2013 설치 [link]
3. 설치가 완료되면 아래 경로의 파일을 다음과 편집.
경로: C:Program Files (x86)SourceForge.netDoxyComment add-in
for Visual StudioDoxyComment.addin
편집내용:
<HostApplication>
<Name>Microsoft Visual Studio</Name>
<Version>13.0</Version> <----- 이 부분을 12.0 으로 수정해야 vs2013에서 add-in 인식이 된다.
</HostApplication>
60. Javadoc (HTML, on IntelliJ, Eclipse)
• Java 플랫폼에서 많이 사용.
• 대부분의 IDE(Java)가 지원하는
Code Tooltip 포맷은 Javadoc이기
때문에 99% 사용한다고 볼 수 있음.
• 기본 output 포맷은 HTML을 지원
하며 커스텀 툴을 사용한다면
CHM 변환도 가능.
61. /**
* <p>SearchDevice Presenter Implementation.</p>
* @author JiHyung Lee(miksystem, Inc)
* @since 2014.07.22
*/
@EBean
public class SearchDevicePresenterImpl
extends AbstractBasePresenter<SearchDeviceView>
implements SearchDevicePresenter, RemoteDeviceEventListener {
/** Remote Device Manager */
@Bean
protected RemoteDeviceManager mDeviceManager;
/**
* Constructor
*
* @param ctx {@link
com.kt.android.silverpia.master.ui.activity.searchdevice.SearchDeviceView}를 상속 받은 컨
텍스트 액티비티.
*/
public SearchDevicePresenterImpl(Context ctx) { super((SearchDeviceView) ctx); }
• 주석은 좌측과 같이 작성.
• Doxygen 포맷과 유사.
• HTML 태그 포함 가능.
Javadoc (Java, HTML Document Generator)
64. Javadoc (on Eclipse)
• IntelliJ IDEA나 Eclipse에서 Javadoc
생성시 설정 가능한 옵션은 큰 차이가
없는데, 이는 JDK 설치 폴더 밑에 있는
javadoc.exe를 이용하여 문서를 생성하기
때문이다.
• IDE마다 생성되는 Javadoc의 테마가 다른
이유는 문서 생성시 IDE가 stylesheet.css를
덮어 씌우기 때문이다.
65. YUIDoc (Javascript, HTML Document Generator)
• Javascript 전용 코드 문서화 툴.
• Node.js 어플리케이션으로
배포 중
• npm이 설치되어 있다면 쉽게 사
용 가능.
• Node.js를 이용한 Server Mode를
지원하여, 실시간 API Reference
Page를 구성 할 수 있음.
66. 1. Node.js 설치 [link]
2. CMD에서 “npm –g I yuidocjs” 실행 [link]
3. js 소스 최상위 루트 폴더로 이동 후 “yuidoc . –c <json.file>” 실행.
아래와 같이 옵션 json 파일을 구성해서 argument로도 전달 가능.
// config.json
{
"name": "The Foo API",
"description": "The Foo API: a library for doing X, Y, and Z",
"version": "1.2.1",
"url": "http://example.com/",
"options": {
"outdir": "../build/apidocs"
}
}
YuiDoc (Javascript, HTML Document Generator)