rubyonrails.org에서 더 보기:

이 파일을 GITHUB에서 읽지 마세요. 가이드는 https://guides.rubyonrails.org 에서 제공됩니다.

API 문서 가이드라인

이 가이드는 Ruby on Rails API 문서 가이드라인을 설명합니다.

이 가이드를 읽은 후에는 다음 내용을 알 수 있습니다:

  • 문서화 목적으로 효과적인 글을 작성하는 방법
  • 다양한 종류의 Ruby 코드를 문서화하기 위한 스타일 가이드라인

1 RDoc

Rails API 문서RDoc으로 생성됩니다. 문서를 생성하려면 rails 루트 디렉토리에 있는지 확인하고, bundle install을 실행한 후 다음을 실행하세요:

$ bundle exec rake rdoc

생성된 HTML 파일은 ./doc/rdoc 디렉토리에서 찾을 수 있습니다.

문법에 대한 도움이 필요하면 RDoc Markup Reference를 참고하세요.

Rails API 문서는 GitHub에서 보는 것이 목적이 아니므로, 링크는 현재 API를 기준으로 RDoc link markup 마크업을 사용해야 합니다.

이는 GitHub Markdown과 api.rubyonrails.orgedgeapi.rubyonrails.org에 게시되는 생성된 RDoc 간의 차이 때문입니다.

예를 들어, RDoc에 의해 생성된 ActiveRecord::Base 클래스로 링크를 만들기 위해 [link:classes/ActiveRecord/Base.html]를 사용합니다.

이는 독자를 현재 문서 버전 밖으로 이동시킬 수 있는(예: edgeapi.rubyonrails.org) [https://api.rubyonrails.org/classes/ActiveRecord/Base.html]와 같은 절대 URL보다 선호됩니다.

3 Wording

간단하고 명확한 문장을 작성하세요. 간결함이 장점입니다. 핵심을 파악하세요.

# BAD
# Caching이 코드 변경 결과를 볼 수 있는 것을 방해할 수 있습니다.

# GOOD 
# Caching은 코드 변경 결과를 보는 것을 방해합니다.

This is korean translation of writing guide.

현재형 사용하기:

  • "You implement" 대신에 "Then you will implement"와 같이 미래형을 사용하지 마세요.
  • 정의되는 시점과 설명되는 시점이 동일할 때 현재형을 사용하세요.
  • 앞의 동작으로 인한 결과를 설명할 때 현재완료형을 사용하세요.
  • 조건문에서는 "When x happens, y happens"와 같이 현재형을 사용하세요.

예시:

# Bad
The client will connect to the server.
Note that the host will be specified as an argument.
The session expires after 30 minutes.

# Good
The client connects to the server.  
Note that the host is specified as an argument.  
When the session expires after 30 minutes, the user must authenticate again.
# BAD
# 해시를 반환했다...
# 해시를 반환할 것이다...
# 해시를 반환한다...

# GOOD
# hash를 반환합니다...

첫 단어를 대문자로 시작하세요. 일반적인 구두점 규칙을 따르세요:

# BAD
# 내부적으로 이름이 지정된 instance variable이 지원하는 
# attribute reader를 선언함

# GOOD 
# 내부적으로 이름이 지정된 instance variable이 지원하는 
# attribute reader를 선언합니다.

현재의 작업 방식을 독자에게 명시적으로든 암묵적으로든 전달하세요. edge에서 권장하는 관용구를 사용하세요. 필요한 경우 선호되는 접근 방식을 강조하기 위해 섹션을 재정렬하는 등의 작업을 하세요. 문서는 모범 사례와 표준적이고 현대적인 Rails 사용법의 모델이 되어야 합니다.

# 나쁜 예
# Book.where('name = ?', "Where the Wild Things Are")
# Book.where('year_published < ?', 50.years.ago)

# 좋은 예
# Book.where(name: "Where the Wild Things Are")
# Book.where(year_published: ...50.years.ago)

문서는 간단하면서도 포괄적이어야 합니다. 엣지 케이스들을 탐색하고 문서화하세요. module이 anonymous인 경우는 어떻게 될까요? collection이 비어있다면 어떨까요? argument가 nil이라면 어떨까요?

3.1 네이밍

Rails 컴포넌트의 올바른 이름은 "Active Support"와 같이 단어 사이에 공백이 있습니다. ActiveRecord는 Ruby 모듈이고, Active Record는 ORM입니다. 모든 Rails 문서는 Rails 컴포넌트를 일관되게 올바른 이름으로 참조해야 합니다.

# GOOD
# Active Record 클래스는 ActiveRecord::Base를 
# 상속받아서 생성할 수 있습니다.

"engine"이나 "plugin"과 대비되는 의미로 "Rails application"을 언급할 때는 항상 "application"이라는 용어를 사용하세요. Rails application은 서비스 지향 아키텍처(service-oriented architecture)를 구체적으로 논의하는 경우가 아니라면 "service"가 아닙니다.

# 나쁨
# Production 서비스들은 upstream으로 상태를 보고할 수 있습니다.
# Devise는 Rails authentication 애플리케이션입니다.

# 좋음
# Production 애플리케이션들은 upstream으로 상태를 보고할 수 있습니다. 
# Devise는 Rails authentication engine입니다.

소프트웨어 이름을 정확하게 표기하세요. 확실하지 않다면 공식 문서와 같은 신뢰할 수 있는 출처를 참고해주세요.

# GOOD
# Arel
# ERB
# Hotwire 
# HTML
# JavaScript
# minitest
# MySQL
# npm
# PostgreSQL
# RSpec

Use the article "an" for "SQL":

SQL 앞에는 "an"이라는 관사를 사용하세요:

# BAD
# SQL 구문을 생성합니다.
# SQLite 데이터베이스를 시작합니다.

# GOOD
# SQL 구문을 생성합니다. 
# SQLite 데이터베이스를 시작합니다.

3.2 대명사

"you"와 "your"의 사용을 피하는 표현을 선호합니다.

# BAD 
# 콜백에서 +return+ 구문을 사용해야 한다면,
# 명시적으로 메서드로 정의하는 것이 좋습니다.

# GOOD
# +return+이 필요한 경우에는 명시적으로 메서드를 
# 정의하는 것이 권장됩니다.

다만, "세션 쿠키를 가진 사용자"와 같이 가상의 사람을 지칭하는 대명사를 사용할 때는 성 중립적인 대명사(they/their/them)를 사용해야 합니다. 다음과 같이 사용하세요:

  • he 또는 she... they를 사용하세요.
  • him 또는 her... them을 사용하세요.
  • his 또는 her... their를 사용하세요.
  • his 또는 hers... theirs를 사용하세요.
  • himself 또는 herself... themselves를 사용하세요.

3.3 미국식 영어

미국식 영어를 사용해주세요 (color, center, modularize 등). 미국식과 영국식 영어 철자 차이 목록을 참고하세요.

3.4 Oxford Comma

Oxford comma를 사용해주세요 ("red, white, and blue"와 같이 사용하고 "red, white and blue"처럼 사용하지 마세요).

4 예제 코드

기본적인 내용과 흥미로운 점 또는 주의할 점을 설명하고 다루는 의미 있는 예제를 선택하세요.

올바른 렌더링을 위해 코드를 왼쪽 여백에서 두 칸 들여쓰기 하세요. 예제들은 Rails coding conventions를 따라야 합니다.

짧은 문서는 코드 스니펫을 소개하기 위한 명시적인 "Examples" 레이블이 필요하지 않습니다. 단락 뒤에 바로 이어서 작성하면 됩니다:

# 요소의 컬렉션을 모든 요소에 대해 +to_s+를 호출하고 
# 이를 결합하여 형식이 지정된 문자열로 변환합니다.
#
#   Blog.all.to_fs # => "First PostSecond PostThird Post" 

반면에 구조화된 문서의 큰 덩어리는 별도의 "Examples" 섹션을 가질 수 있습니다:

# ==== 예시
#
#   Person.exists?(5)
#   Person.exists?('5')
#   Person.exists?(name: "David")
#   Person.exists?(['name LIKE ?', "%#{query}%"])

표현식의 결과는 "# => "로 표시되며 수직으로 정렬됩니다:

# 정수가 짝수인지 홀수인지 체크하는 메소드입니다.
#
#   1.even? # => false 
#   1.odd?  # => true
#   2.even? # => true
#   2.odd?  # => false

줄이 너무 긴 경우, 주석은 다음 줄에 배치될 수 있습니다:

#   label(:article, :title)
#   # => <label for="article_title">Title</label>
#
#   label(:article, :title, "A short title")  
#   # => <label for="article_title">A short title</label>
#
#   label(:article, :title, "A short title", class: "title_label")
#   # => <label for="article_title" class="title_label">A short title</label>

디버깅 목적으로 putsp와 같은 출력 메서드를 사용하는 것을 피하세요.

반면에 일반적인 주석은 화살표를 사용하지 않습니다:

#   polymorphic_url(record)  # comment_url(record)와 동일

4.1 SQL

SQL 구문을 문서화할 때는 출력 결과 앞에 => 를 붙이지 않아야 합니다.

예시:

#   User.where(name: 'Oscar').to_sql
#   # SELECT "users".* FROM "users"  WHERE "users"."name" = 'Oscar'

4.2 IRB

Ruby의 대화형 REPL인 IRB의 동작을 문서화할 때는 항상 명령어 앞에 irb>를 붙여야 합니다. 출력은 =>로 시작해야 합니다.

예를 들어,

# primary key(id)가 10인 customer를 찾습니다.
#   irb> customer = Customer.find(10)
#   # => #<Customer id: 10, first_name: "Ryan">

4.3 Bash / 커맨드라인

커맨드라인 예시의 경우, 항상 명령어 앞에 $를 붙여주세요. 출력 결과에는 아무것도 붙일 필요가 없습니다.

# 다음 명령어를 실행하세요:
#   $ bin/rails new zomg 
#   ...

5 Booleans

predicate와 flag의 경우, 정확한 값보다는 boolean의 의미를 문서화하는 것이 좋습니다.

Ruby에서 정의된 대로 "true" 또는 "false"를 사용할 때는 일반 글꼴을 사용하세요. 싱글톤 truefalse는 고정폭 글꼴을 사용해야 합니다. "truthy"와 같은 용어는 피해주세요. Ruby는 언어에서 무엇이 true이고 false인지 정의하고 있으므로, 이 단어들은 기술적인 의미를 가지며 대체어가 필요하지 않습니다.

일반적인 규칙으로, 절대적으로 필요한 경우가 아니라면 싱글톤을 문서화하지 마세요. 이는 !!나 삼항 연산자와 같은 인위적인 구조를 방지하고, 리팩토링을 허용하며, 구현에서 호출되는 메서드가 반환하는 정확한 값에 코드가 의존할 필요가 없게 합니다.

예시:

# +config.action_mailer.perform_deliveries+는 메일이 실제로 
# 전송될지 여부를 지정하며 기본값은 true입니다

사용자는 flag의 실제 기본값이 무엇인지 알 필요가 없으므로, 우리는 단지 그것의 boolean 의미만 문서화합니다.

predicate의 예시:

# 컬렉션이 비어있으면 true를 반환합니다.
#
# 컬렉션이 로드되었다면 +collection.size.zero?+와 동일합니다.
# 컬렉션이 로드되지 않았다면 +!collection.exists?+와 동일합니다. 
# 컬렉션이 아직 로드되지 않았고 레코드를 어차피 가져올 예정이라면 
# +collection.length.zero?+를 확인하는 것이 더 좋습니다.
def empty?
  if loaded?
    size.zero? 
  else
    @target.blank? && !scope.exists?
  end
end

API는 어떤 특정한 값을 제공하지 않도록 주의하며, 메소드는 predicate 의미론을 가지므로 이것으로 충분합니다.

6 File Names

경험적으로, 어플리케이션 루트를 기준으로 한 파일명을 사용하세요: routes.rbRAILS_ROOT/config/routes.rb 대신 config/routes.rb를 사용하세요.

7 Fonts

7.1 Fixed-width Font

다음의 경우 fixed-width 폰트를 사용하세요:

  • 상수, 특히 class와 module 이름
  • Method 이름
  • nil, false, true, self와 같은 literal
  • Symbol
  • Method 파라미터
  • 파일 이름
  • HTML tag와 attribute
  • CSS selector, attribute와 value
class Array
  # 모든 요소들에 대해 +to_param+을 호출하고 결과를 슬래시로 연결합니다.
  # Action Pack의 +url_for+에서 사용됩니다.
  def to_param
    collect { |e| e.to_param }.join "/"
  end
end

+...+를 고정폭 폰트로 사용하는 것은 일반적인 클래스, 모듈, 메서드 이름, 심볼, 경로(forward slash 포함) 등과 같은 간단한 내용에만 작동합니다. 다른 모든 것에는 <tt>...</tt>를 사용하세요.

다음 명령어로 RDoc 출력을 빠르게 테스트할 수 있습니다:

$ echo "+:to_param+" | rdoc --pipe
# => <p><code>:to_param</code></p>

예를 들어, 공백이나 따옴표가 있는 코드는 <tt>...</tt> 형식을 사용해야 합니다.

7.2 일반 폰트

"true"와 "false"가 Ruby 키워드가 아닌 일반적인 영어 단어로 사용될 때는 일반 폰트를 사용합니다:

# 지정된 context 내에서 모든 validation을 실행합니다.
# 오류가 발견되지 않으면 true를 반환하고, 그렇지 않으면 false를 반환합니다.
#
# 인자가 false인 경우(기본값은 +nil+), context는 
# <tt>new_record?</tt>가 true이면 <tt>:create</tt>로 설정되고
# 그렇지 않으면 <tt>:update</tt>로 설정됩니다.
#
# <tt>:on</tt> 옵션이 없는 validation은 context에 관계없이
# 실행됩니다. <tt>:on</tt> 옵션이 있는 validation은 
# 지정된 context에서만 실행됩니다.
def valid?(context = nil)
  # ...
end

8 설명 목록

옵션, 파라미터 등의 목록에서는 항목과 설명 사이에 하이픈을 사용하세요 (일반적으로 옵션이 기호이기 때문에 콜론보다 더 읽기 좋습니다):

# * <tt>:allow_nil</tt> - 속성이 +nil+일 경우 유효성 검사를 건너뜁니다.

설명은 대문자로 시작하고 마침표로 끝납니다 - 이는 표준 영어입니다.

추가적인 세부 사항과 예시를 제공하고자 할 때는 option section 스타일을 사용하는 것이 대안이 될 수 있습니다.

[ActiveSupport::MessageEncryptor#encrypt_and_sign][#encrypt_and_sign]가 이것의 좋은 예시입니다.

# ==== Options
#
# [+:expires_at+]
#   메시지가 만료되는 날짜/시간입니다. 이 날짜/시간이 지나면
#   메시지 검증이 실패합니다.
#
#     message = encryptor.encrypt_and_sign("hello", expires_at: Time.now.tomorrow)
#     encryptor.decrypt_and_verify(message) # => "hello"
#     # 24시간 후...
#     encryptor.decrypt_and_verify(message) # => nil

9 동적으로 생성된 메소드

(module|class)_eval(STRING)으로 생성된 메소드들은 생성된 코드의 인스턴스와 함께 옆에 주석이 있습니다. 그 주석은 템플릿으로부터 2칸 띄워져 있습니다:

(module|class)_eval(STRING) 코드 주석

결과 라인이 200컬럼 이상처럼 너무 넓은 경우, 주석을 호출문 위에 작성합니다:

# def self.find_by_login_and_activated(*args)
#   options = args.extract_options!
#   ...
# end
self.class_eval %{
  def self.#{method_id}(*args) 
    options = args.extract_options!
    ...
  end
}, __FILE__, __LINE__

10 Method Visibility

Rails의 문서를 작성할 때, user-facing API와 internal API를 구분하는 것이 중요합니다.

Ruby의 private scope에 있는 메소드들은 user-facing API에서 제외됩니다. 하나 framework 내의 다른 곳에서 호출될 수 있도록 일부 internal API 메소드들은 Ruby의 public scope에 있어야 합니다. 이러한 메소드들을 user-facing API에서 제외하려면 RDoc의 :nodoc: 지시어를 사용하세요.

예시로 ActiveRecord::Core::ClassMethods#arel_table가 있습니다:

module ActiveRecord::Core::ClassMethods
  def arel_table # :nodoc: 
    # 매직을 수행합니다..
  end
end

비록 public 메서드이지만, 사용자들은 이를 사용하는 것에 의존해서는 안됩니다. 이 메서드의 이름이 변경되거나, 반환값이 변경되거나, 메서드가 완전히 제거될 수 있습니다. :nodoc:으로 표시함으로써 사용자에게 공개되는 API 문서에서 제거됩니다.

기여자로서 API가 사용자에게 공개되어야 하는지 또는 내부용이어야 하는지에 대해 생각하는 것이 중요합니다. Rails 팀은 완전한 deprecation 주기를 거치지 않고는 사용자에게 공개된 API에 breaking change를 만들지 않기로 약속했습니다. 따라서 이미 private이 아닌 내부 메서드나 모듈에는 :nodoc:을 추가해야 합니다. (모듈이나 클래스에 :nodoc:을 추가하는 것은 모든 메서드가 내부 API임을 나타내며, 사용자에게 공개되는 API 문서에서 제거되어야 합니다.)

11 Rails Stack에 대하여

Rails API의 부분들을 문서화할 때, 전체 Rails stack을 고려하는 것이 중요합니다. 여러분이 문서화하는 메서드나 클래스의 동작은 컨텍스트에 따라 달라질 수 있습니다.

이러한 예시 중 하나가 ActionView::Helpers::AssetTagHelper#image_tag입니다:

# image_tag("icon.png") 
#   # => <img src="/assets/icon.png" /> 

단독으로는 image_tag/images/icon.png를 반환할 것입니다. 하지만 Asset Pipeline을 포함한 전체 Rails 스택을 고려하면, 위와 같은 결과를 볼 수 있습니다.

우리는 단순히 독립된 메서드가 아닌, 프레임워크 전체의 동작을 문서화하고자 합니다. 우리의 관심사는 사용자가 기본 Rails 스택 전체를 사용할 때 경험하게 되는 동작입니다.

Rails 팀이 특정 API를 어떻게 다루는지에 대한 질문이 있다면, 이슈 트래커에 티켓을 열거나 패치를 보내는 것을 망설이지 마세요.



맨 위로