코드 문서화

코드 문서화 (Documenting Code)

API를 만들고 나면, 그 사용법을 설명하는 문서가 필요해요. Crystal은 코드 주석을 기반으로 API 문서를 자동으로 생성해 주기 때문에, 문서 작성을 위한 별도 파일 없이 코드만 잘 주석 달아두면 돼요.

출처: Crystal 공식 문서

본문

API 기능에 대한 문서는 해당 기능의 정의 바로 앞에 오는 코드 주석에 작성할 수 있어요.

기본적으로 모든 public 메서드·매크로·타입·상수는 API 문서의 일부로 간주돼요. lib 타입과 non-public 기능은 기본적으로 제외돼요. 이 포함 여부는 :nodoc::showdoc: 지시어로 설정할 수 있어요.

TIP: 컴파일러 명령 crystal docs가 API 문서를 자동으로 추출해 웹사이트로 만들어 줘요.

연관 규칙

문서 주석은 문서화할 기능의 정의 바로 위에 위치해야 해요. 연속된 주석 줄은 하나의 주석 블록으로 합쳐지고, 빈 줄이 나오면 문서화 기능과의 연관이 끊어져요.

# This comment is not associated with the class.

# First line of documentation for class Unicorn.
# Second line of documentation for class Unicorn.
class Unicorn
end

형식

문서 주석은 Markdown 형식을 지원해요.

문서 주석의 첫 문단이 그 요약(summary)으로 간주돼요. 목적과 기능을 간결하게 정의해야 하죠.

보충 설명과 사용법 안내는 그다음 문단에 이어 붙이면 돼요.

예를 들어:

# Returns the number of horns this unicorn has.
#
# Always returns `1`.
def horns
  1
end

TIP: 일반적으로 3인칭 현재형의 서술형을 쓰는 걸 권장해요. Return the number of horns this unicorn has(명령형)보다 Returns the number of horns this unicorn has처럼 쓰라는 뜻이에요.

마크업

링크

다른 API 기능을 가리키는 참조는 백틱 하나로 감싸면 돼요. 그러면 자동으로 그 기능으로 연결되는 링크로 변환돼요.

class Unicorn
  # Creates a new `Unicorn` instance.
  def initialize
  end
end

Crystal 코드와 동일한 탐색 규칙이 적용돼요. 지금 문서화 중인 네임스페이스의 기능은 상대 이름으로 접근할 수 있어요.

  • 인스턴스 메서드는 해시 접두사로 참조해요: #horns.
  • 클래스 메서드는 점 접두사로 참조해요: .new.
  • 상수와 타입은 이름으로 참조해요: Unicorn.

다른 네임스페이스의 기능은 완전한 타입 경로로 참조해요: Unicorn#horns, Unicorn.new, Unicorn::CONST.

메서드의 서로 다른 오버로드는 전체 시그니처로 구분할 수 있어요: .new(name), .new(name, age).

매개변수

매개변수를 가리킬 때는 그 이름을 _기울임꼴_로 쓰는 걸 권장해요(*italicized*).

# Creates a unicorn with the specified number of *horns*.
def initialize(@horns = 1)
  raise "Not a unicorn" if @horns != 1
end

코드 예시

코드 예시는 Markdown 코드 블록에 넣을 수 있어요. 언어 태그가 없으면 그 코드 블록은 Crystal 코드로 간주돼요.

# Example:
# ```
# unicorn = Unicorn.new
# unicorn.horns # => 1
# ```
class Unicorn
end

코드 블록을 일반 텍스트로 지정하려면 명시적으로 태그를 붙여야 해요.

# Output:
# ```plain
# "I'm a unicorn"
# ```
def say
  puts "I'm a unicorn"
end

다른 언어 태그도 쓸 수 있어요.

코드 블록 안에서 표현식의 값을 보여주려면 # =>를 쓰면 돼요.

1 + 2             # => 3
Unicorn.new.speak # => "I'm a unicorn"

어드모니션(Admonitions)

문제·주의·가능한 이슈를 시각적으로 강조하기 위해 여러 어드모니션 키워드를 지원해요.

  • BUG
  • DEPRECATED
  • EXPERIMENTAL
  • FIXME
  • NOTE
  • OPTIMIZE
  • TODO
  • WARNING

어드모니션 키워드는 해당 줄의 첫 단어여야 하고 전부 대문자여야 해요. 가독성을 위해 선택적으로 콜론을 붙이는 걸 권장해요.

# Makes the unicorn speak to STDOUT
#
# NOTE: Although unicorns don't normally talk, this one is special
# TODO: Check if unicorn is asleep and raise exception if not able to speak
# TODO: Create another `speak` method that takes and prints a string
def speak
  puts "I'm a unicorn"
end

# Makes the unicorn talk to STDOUT
#
# DEPRECATED: Use `speak`
def talk
  puts "I'm a unicorn"
end

컴파일러는 몇 가지 어드모니션을 문서 주석에 암시적으로 추가해요.

  • @[Deprecated] 어노테이션은 DEPRECATED 어드모니션을 추가해요.
  • @[Experimental] 어노테이션은 EXPERIMENTAL 어드모니션을 추가해요.

지시어(Directives)

지시어는 문서 생성기가 특정 기능의 문서를 어떻게 다룰지 알려줘요.

ditto

연속해서 정의된 두 기능이 같은 문서를 갖는다면, :ditto:를 써서 이전 정의의 문서 주석을 그대로 복사할 수 있어요.

# Returns the number of horns.
def horns
  horns
end

# :ditto:
def number_of_horns
  horns
end

지시어는 별도 줄에 있어야 하지만, 그 외 줄에 추가 문서를 더 쓸 수 있어요. :ditto: 지시어는 단순히 이전 문서 주석의 내용으로 대체돼요.

nodoc

:nodoc: 지시어로 public 기능을 API 문서에서 숨길 수 있어요. private·protected 기능은 항상 숨겨져요.

# :nodoc:
class InternalHelper
end

이 지시어는 문서 주석의 첫 줄에 있어야 해요. 앞 공백은 선택이에요. 그다음 주석 줄은 내부 문서용으로 쓸 수 있어요.

showdoc

:showdoc: 지시어는 평소에는 문서화되지 않는 타입과 메서드를 API 문서에 포함시켜요. private·protected 기능과 lib 타입에 적용해 API 문서에 나타나게 할 수 있어요.

아래 예시에서 Foo의 API 문서는 private 메서드임에도 Foo.foo를 포함해요.

module Foo
  # :showdoc:
  #
  # This private method is part of the API docs.
  private def self.foo
  end
end

이 지시어는 문서 주석의 첫 줄에 있어야 해요. 앞 공백은 선택이에요. 그다음 주석 줄은 문서 내용으로 쓰여요.

lib 타입에 적용하면 lib 네임스페이스 안의 모든 기능(funs, types, variables 등)이 API 문서에 포함돼요. 다만 개별 기능은 :nodoc:로 명시적으로 제외할 수 있어요.

# :showdoc:
#
# This lib type and all features inside are part of the API docs.
lib LibFoo
  # Documentation for bar
  fun bar : Void

  # :nodoc:
  # baz is not part of the API docs
  fun baz : Void

  # Documentation for FooEnum
  enum FooEnum
    Member1
    Member2
    Member3
  end

  # Documentation for FooStruct
  struct FooStruct
    var_1 : Int32
    var_2 : Int32
  end
end

부모 네임스페이스가 문서화되지 않았다면, 중첩된 :showdoc: 지시어는 효과가 없어요.

# :nodoc:
struct MyStruct
  # :showdoc:
  #
  # This showdoc directive has no effect because the MyStruct namespace is nodoc.
  struct MyStructChild
  end
end

# Implicitly nodoc
lib LibFoo
  # :showdoc:
  #
  # This showdoc directive has no effect because the LibFoo namespace is implicitly undocumented.
  # If LibFoo had a showdoc directive, the showdoc directive here would be redundant.
  fun bar : Void
end

inherit

문서 상속을 참고하세요.

문서 상속

인스턴스 메서드에 문서 주석이 없는데, 부모 타입에 같은 시그니처의 메서드가 존재한다면 부모 메서드에서 문서를 상속받아요.

예를 들어:

abstract class Animal
  # Returns the name of `self`.
  abstract def name : String
end

class Unicorn < Animal
  def name : String
    "unicorn"
  end
end

Unicorn#name의 문서는 다음과 같아요.

Description copied from class `Animal`

Returns the name of `self`.

자식 메서드는 :inherit:를 써서 Description copied from ... 텍스트 없이 부모의 문서를 명시적으로 복사할 수도 있어요. 또는 :inherit:를 써서 부모 문서를 자식의 추가 문서로 주입할 수도 있어요.

예를 들어:

abstract class Parent
  # Some documentation common to every *id*.
  abstract def id : Int32
end

class Child < Parent
  # Some documentation specific to *id*'s usage within `Child`.
  #
  # :inherit:
  def id : Int32
    -1
  end
end

Child#id의 문서는 다음과 같아요.

Some documentation specific to _id_'s usage within `Child`.

Some documentation common to every _id_.

NOTE: 문서 상속은 인스턴스 메서드 중 생성자가 아닌 메서드에서만 동작해요.

완전한 예시

# A unicorn is a **legendary animal** (see the `Legendary` module) that has been
# described since antiquity as a beast with a large, spiraling horn projecting
# from its forehead.
#
# To create a unicorn:
#
# ```
# unicorn = Unicorn.new
# unicorn.speak
# ```
#
# The above produces:
#
# ```text
# "I'm a unicorn"
# ```
#
# Check the number of horns with `#horns`.
class Unicorn
  include Legendary

  # Creates a unicorn with the specified number of *horns*.
  def initialize(@horns = 1)
    raise "Not a unicorn" if @horns != 1
  end

  # Returns the number of horns this unicorn has
  #
  # ```
  # Unicorn.new.horns # => 1
  # ```
  def horns
    @horns
  end

  # :ditto:
  def number_of_horns
    horns
  end

  # Makes the unicorn speak to STDOUT
  def speak
    puts "I'm a unicorn"
  end

  # :nodoc:
  class Helper
  end
end

더 알아보기

  • API 문서는 주석을 기반으로 자동 생성되므로, 코드 주석을 잘 남기는 것이 곧 문서 관리예요.
  • :nodoc::showdoc:의 차이를 기억해 두면 좋아요. public 기능을 숨기느냐, 평소 문서화되지 않는 기능을 드러내느냐의 차이죠.
  • 문서 상속은 인스턴스 메서드(생성자 제외)에서만 동작한다는 점을 주의하세요.