원문: Effective Scala - Formatting
Code formatting의 구체적인 사항들은 실용적이기만 하다면 크게 문제가 되지 않습니다. 본질적으로 스타일은 좋거나 나쁜 것이 아니며, 거의 모든 사람들이 각자의 선호에 따라 좋고 나쁨을 다르게 느낍니다. 하지만 어플리케이션을 일관적으로 동일한 포맷으로 작성하게 되면 가독성이 향상됩니다. 특정 스타일에 이미 익숙한 독자는 새로운 규칙이나 언어 문법의 복잡한 부분을 다시 이해할 필요가 없습니다.
스칼라에서는 이러한 일관성이 특히 중요한데, 그 이유는 스칼라 문법이 굉장히 높은 수준의 중첩(중복)을 가지기 때문입니다. 하나 예를 들자면 메소드의 호출이 있습니다. 메소드는 "."으로 호출할 수 있고, 공백으로 호출할 수도 있고, 매개변수가 없거나 매개변수가 하나인 메서드는 괄호를 사용해 호출하거나 심지어 괄호 없이 호출할 수도 있습니다. 이에 더해서, 메소드 호출을 위한 다양한 스타일은 문법에 있는 또 다른 모호성을 드러내기도 합니다! 신중히 선택한 formatting 스타일을 사용해 작성한 일관적인 어플리케이션은 사람과 기계 모두에게 모호성을 없애줍니다.
우리는 스칼라 스타일 가이드를 따르며, 추가적으로 다음의 규칙들을 적용합니다.
Whitespace (공백)
- 두 칸 들여쓰기를 사용합니다
- 100 자를 초과하는 줄은 최대한 피하려고 노력합니다
- method, class, object 정의 사이에는 빈 줄을 한 줄 추가합니다
class Formatting {
// 두 줄 들여쓰기
...
// 100 자를 넘어가면 새롭게 함수를 만드는 식으로 우회합시다
def myLongFunction(test: Boolean) = {
val res = if (test) test else veryLongFunction()
res
}
def veryLongFunction(): Boolean {
???
}
} // 새로운 class 정의가 있다면 한 줄 띄웁시다
class NewFormatting {
// ...
}
Naming (이름)
- 작은 범위에서는 짧은 이름을 사용하세요
- i, j, k는 반복문 안에서만 사용하기로 합시다
- 큰 범위에서는 긴 이름을 사용하세요
- 외부 API에는 의미를 전달할 수 있는 더 길고 설명할 수 있는 이름을 사용하세요.
예를 들어, Future.collect가 Future.all보다 낫습니다.
(설명하자면, collect는 모든 Future를 수집한다는 의미를 나타내지만, all은 어떤 동작인지 설명이 되지 않습니다)
- 외부 API에는 의미를 전달할 수 있는 더 길고 설명할 수 있는 이름을 사용하세요.
- 공통적인 약어를 사용하되 난해한 약어는 피하세요
- 모두가 ok, err, defn 같은 약어는 알고 있지만, stri 같은 약어는 알지 못합니다
- 같은 이름을 다른 용도로 재사용하지 마세요
- val을 사용하세요
(변수 하나에 여러 의미를 담지 말라는 뜻입니다)
- val을 사용하세요
- 백틱(`)을 사용해 예약어를 overload 하지 마세요
- `type`을 쓰는것보다 typ를 사용하세요
- Side effect가 있는 연산에는 능동적인 이름을 사용하세요
- user.setActive() 보다 user.activate() 가 낫습니다
- 값을 반환하는 메소드에는 설명적인 이름을 사용하세요
- src.defined 보다 src.isDefined 가 낫습니다
- getter에 get을 접두사로 사용하지 마세요
- 위의 규칙과 중복됩니다. site.count가 site.getCount보다 낫습니다.
- 패키지나 객체 이름에 이미 포함된 이름을 반복하지 마세요
- User.getUser는 User.get과 정보 측면에서 어떤 차이도 없습니다
// 아래처럼 적으세요
object User {
def get(id: Int): Option[User]
}
// 이렇게 하지 마세요
object User {
def getUser(id: Int): Option[User]
}
Imports (임포트)
- 임포트 줄은 알파벳 순서로 정렬하세요
- 시각적으로 쉽게 확인할 수 있으며, 자동화하기도 편합니다
- 하나의 패키지에서 여러 이름을 임포트할 때는 중괄호를 사용하세요
- 예시: import com.twitter.concurrent.{Broker, Offer}
- 여섯 개가 넘는 이름을 임포트한다면 와일드 카드를 사용하세요
- 예시: import com.twitter.concurrent._
- 단, 일부 패키지는 엄청나게 많은 이름을 내보내므로 이를 맹목적으로 사용하지는 마세요
- collection을 사용할 때는 scala.collection.immutable 또는 scala.collection.mutable을 명시적으로 임포트하세요
- 가변 및 불변 컬렉션은 동일한 이름을 가질 수 있으므로, 이름을 명시하면 독자들은 어떤 것을 사용했는지 분명하게 이해할 수 있습니다. (예: immutable.Map)
- 다른 패키지를 통해 상대적인 임포트를 사용하지 마세요
- 다음처럼 사용하지 마세요
import com.twitter
import concurrent - 이렇게 사용하세요
import com.twitter.concurrent
- 다음처럼 사용하지 마세요
- 임포트는 파일 상단에 배치하세요
- 독자가 모든 임포트를 한 번에 참조할 수 있도록 합니다
Braces (중괄호)
중괄호는 복합 표현을 생성하는데 사용되며 ("모듈 언어"에서는 다른 용도로 사용됩니다), 복합 표현의 값은 목록에 적힌 expression의 가장 마지막 줄입니다.
중괄호를 사용하면 method body를 명확히 구분할 수 있지만, 간단한 표현식에는 중괄호 사용을 피하세요.
// 이렇게 쓰세요
def sqaure(x: Int) = x * x
// 이렇게 쓰지 마세요
def sqaure(x: Int) = {
x * x
}
첫 번째 방식이 더 간결하고 읽기 쉽습니다. 명확하지 않다면 불필요한 구문적 장식을 피하세요.
Pattern matching (패턴 매칭)
패턴 매칭은 가능하다면 언제든지 함수 정의에 직접 사용하세요.
// 이렇게 사용하세요
val list: Seq[Option[String]] = ???
list map {
case Some(x) => x
case None => default
}
// 이렇게 사용하지 마세요
list map { item =>
item match {
case Some(x) => x
case None => default
}
}
리스트 항목을 순회하고 있다는 점이 명확해지면서 불필요한 간접 접근도 사라집니다.
Comments (주석)
Scala Doc을 사용해 API 문서를 제공하세요.
// 이렇게 제공하세요
/**
* ServiceBuilder builds services
* ..
*/
// 이렇게 하지 마세요
/** ServiceBuilder builds services
* ...
*/
ASCII Art나 기타 시각적 장식을 사용하지 마세요.
API는 문서화하되, 불필요한 주석을 추가하지 마세요.
코드의 동작을 설명하는 주석을 추가하게 되었다면, 먼저 그 동작이 명확해질 수 있도록 코드를 재구성할 수 있는지 물어보세요. "당연히 작동한다"라는 문장이 "작동한다, 당연히" 보다 낫습니다. (Hoare의 표현을 인용한 문장입니다)
위에 적은 포맷팅 규칙은 모두 scalafmt라는 Scala code formatter를 사용해 해결할 수 있습니다.
Intellij에는 기본으로 포함되어 있어서 쉽게 적용할 수 있습니다.
자세한 사항은 링크를 통해 확인하세요.