본문으로 건너뛰기

코드 포매팅 가이드라인

이 문서는 SideStore 프로젝트에서 Swift와 Objective-C 코드를 포맷팅하는 기준을 정리했어요. 일관된 포맷팅은 코드베이스를 더 읽기 쉽고 관리하기 편하게 만들어 줘요.

일반 원칙

  • 일관성: 프로젝트에서 이미 사용 중인 스타일을 따라 주세요.
  • 가독성: 코드는 쉽게 읽히고 이해돼야 해요.
  • 단순함: 복잡한 해결책보다 단순하고 명확한 방식을 선호해 주세요.
  • Apple 관례: 가능하면 Apple이 권장하는 Swift/Objective-C 관례를 따라 주세요.

Swift 포매팅

네이밍 규칙

변수와 함수

  • 변수, 함수, 메서드에는 camelCase를 사용해 주세요.
  • 목적이 명확히 드러나도록 설명적인 이름을 붙여 주세요.
// ✅ 좋아요
let downloadProgress: Float
func validateUserCredentials() -> Bool
var isAppInstalling: Bool

// ❌ 나빠요
let dp: Float
func validate() -> Bool
var installing: Bool

클래스, 구조체, 열거형, 프로토콜

  • 타입 이름에는 PascalCase를 사용해 주세요.
  • 명사 기반의 설명적인 이름을 붙여 주세요.
// ✅ 좋아요
class AppInstaller
struct InstallationProgress
enum AppState
protocol AppManaging

// ❌ 나빠요
class installer
struct progress
enum state
protocol managing

상수

  • 상수에도 camelCase를 사용해 주세요.
  • 전역 상수라면 SCREAMING_SNAKE_CASE를 고려해 주세요.
// ✅ 좋아요
let maxRetryAttempts = 3
private let defaultTimeout: TimeInterval = 30.0

// Global constants
let MAX_CONCURRENT_DOWNLOADS = 5

// ❌ 나빠요
let MaxRetryAttempts = 3
let max_retry_attempts = 3

들여쓰기와 공백

들여쓰기

  • 들여쓰기는 스페이스 4개예요. 탭은 사용하지 말아 주세요.
  • 줄바꿈한 인자는 여는 구분자와 맞춰 주세요.
// ✅ 좋아요
func installApp(withIdentifier identifier: String,
sourceURL: URL,
completion: @escaping (Result<Void, Error>) -> Void) {
// Implementation
}

// ❌ 나빠요
func installApp(withIdentifier identifier: String,
sourceURL: URL,
completion: @escaping (Result<Void, Error>) -> Void) {
// Implementation
}

줄 길이

  • 가능한 한 줄 길이를 120자 이하로 유지해 주세요.
  • 길어지면 의미 단위에서 줄바꿈해 주세요.
// ✅ 좋아요
let longVariableName = SomeClass.createInstanceWithVeryLongMethodName(
parameter1: value1,
parameter2: value2
)

// ❌ 나빠요
let longVariableName = SomeClass.createInstanceWithVeryLongMethodName(parameter1: value1, parameter2: value2, parameter3: value3)

공백

  • 연산자 주변에는 공백 한 칸을 넣어 주세요.
  • 줄 끝 공백은 남기지 말아 주세요.
  • 함수나 큰 코드 블록 사이에는 빈 줄 한 줄을 넣어 주세요.
// ✅ 좋아요
let result = value1 + value2
if condition && anotherCondition {
// Code
}

func firstFunction() {
// Implementation
}

func secondFunction() {
// Implementation
}

// ❌ 나빠요
let result=value1+value2
if condition&&anotherCondition{
// Code
}
func firstFunction(){
// Implementation
}
func secondFunction(){
// Implementation
}

중괄호와 제어 흐름

중괄호 스타일

  • 여는 중괄호는 구문과 같은 줄에 둬 주세요.
  • 닫는 중괄호는 별도 줄에 두고 구문과 맞춰 주세요.
// ✅ 좋아요
if condition {
doSomething()
} else {
doSomethingElse()
}

class MyClass {
func myMethod() {
// Implementation
}
}

// ❌ 나빠요
if condition
{
doSomething()
}
else
{
doSomethingElse()
}

Guard 구문

  • 조기 반환이 필요하면 guard를 사용해 주세요.
  • 조건은 간결하고 읽기 쉽게 유지해 주세요.
// ✅ 좋아요
guard let url = URL(string: urlString) else {
completion(.failure(ValidationError.invalidURL))
return
}

guard !apps.isEmpty else {
return
}

// ❌ 나빠요
if let url = URL(string: urlString) {
// Long nested code block
} else {
completion(.failure(ValidationError.invalidURL))
return
}

타입 표기와 추론

타입 표기 사용 시점

  • 타입이 명확하지 않을 때는 타입을 명시해 주세요.
  • Swift가 분명히 추론할 수 있을 때는 생략해 주세요.
// ✅ 좋아요
let name = "SideStore" // Type is obvious
let timeout: TimeInterval = 30 // Type clarifies intent
var apps: [App] = [] // Empty array needs type annotation

// ❌ 나빠요
let name: String = "SideStore" // Redundant type annotation
let timeout = 30 // Unclear if Int or TimeInterval

함수 선언

파라미터 라벨

  • 설명적인 라벨을 사용해 주세요.
  • 문장이 자연스럽게 읽힌다면 첫 번째 라벨을 생략해 주세요.
// ✅ 좋아요
func install(_ app: App, to device: Device)
func download(from url: URL, completion: @escaping (Data?) -> Void)

// ❌ 나빠요
func install(app: App, device: Device)
func download(url: URL, completion: @escaping (Data?) -> Void)

반환 타입

  • 가능하면 반환 타입을 같은 줄에 둬 주세요.
  • 시그니처가 길다면 새 줄에서 정리해 주세요.
// ✅ 좋아요
func processData() -> Result<ProcessedData, ProcessingError>

func complexFunction(withManyParameters param1: String,
param2: Int,
param3: Bool)
-> Result<ComplexReturnType, ComplexErrorType> {
// Implementation
}

Objective-C 포매팅

네이밍 규칙

메서드

  • 파라미터 라벨이 명확한 설명형 이름을 사용해 주세요.
  • 소문자로 시작하고 camelCase를 사용해 주세요.
// ✅ 좋습니다
- (void)installAppWithIdentifier:(NSString *)identifier
sourceURL:(NSURL *)sourceURL
completion:(void (^)(NSError *error))completion;

// ❌ 나빠요
- (void)install:(NSString *)id url:(NSURL *)url completion:(void (^)(NSError *))completion;

변수와 프로퍼티

  • camelCase를 사용해 주세요.
  • 설명적인 이름을 붙여 주세요.
  • 인스턴스 변수에는 밑줄을 접두사로 붙여 주세요.
// ✅ 좋아요
@interface AppManager : NSObject
@property (nonatomic, strong) NSArray<App *> *installedApps;
@property (nonatomic, assign) BOOL isInstalling;
@end

@implementation AppManager {
NSURLSession *_networkSession;
dispatch_queue_t _processingQueue;
}

클래스와 프로토콜

  • PascalCase를 사용해 주세요.
  • 공개 클래스라면 SS 같은 접두사를 붙이는 걸 고려해 주세요.
// ✅ 좋아요
@interface SSAppInstaller : NSObject
@protocol SSAppManaging <NSObject>

// ❌ 나빠요
@interface appInstaller : NSObject
@protocol appManaging <NSObject>

공백과 포매팅

메서드 선언

  • 파라미터를 세로로 정렬해 주세요.
  • 일관된 공백을 유지해 주세요.
// ✅ 좋아요
- (instancetype)initWithIdentifier:(NSString *)identifier
title:(NSString *)title
version:(NSString *)version;

// ❌ 싫어요
- (instancetype)initWithIdentifier:(NSString *)identifier title:(NSString *)title version:(NSString *)version;

중괄호

  • 여는 중괄호를 같은 줄에 둬 주세요.
  • 닫는 중괄호는 별도 줄에 둬 주세요.
// ✅ 좋아요요
if (condition) {
[self doSomething];
} else {
[self doSomethingElse];
}

// ❌ 나빠요
if (condition)
{
[self doSomething];
}
else
{
[self doSomethingElse];
}

주석과 문서화

Swift 문서 주석

  • 문서 주석에는 ///를 사용해 주세요.
  • 공개 API라면 파라미터와 반환 값을 설명해 주세요.
/// Downloads and installs an app from the specified URL
/// - Parameters:
/// - identifier: The unique identifier for the app
/// - sourceURL: The URL to download the app from
/// - completion: Called when installation completes or fails
/// - Returns: A cancellable operation
func installApp(withIdentifier identifier: String,
sourceURL: URL,
completion: @escaping (Result<Void, Error>) -> Void) -> Operation {
// Implementation
}

Objective-C 문서 주석

  • /** */를 사용해 주세요.
  • HeaderDoc이나 Doxygen 관례를 따라 주세요.
/**
* Downloads and installs an app from the specified URL
* @param identifier The unique identifier for the app
* @param sourceURL The URL to download the app from
* @param completion Block called when installation completes or fails
*/
- (void)installAppWithIdentifier:(NSString *)identifier
sourceURL:(NSURL *)sourceURL
completion:(void (^)(NSError *error))completion;

인라인 주석

  • 한 줄 주석에는 //를 사용해 주세요.
  • 간결하고 필요한 내용만 적어 주세요.
  • 무엇인지보다는 왜 그런지 설명해 주세요.
// ✅ 좋아요
// Retry failed downloads up to 3 times to handle temporary network issues
let maxRetryAttempts = 3

// ❌ 나빠요
// Set maxRetryAttempts to 3
let maxRetryAttempts = 3

오류 처리

Swift 오류 처리

  • Swift의 throwsResult를 활용해 주세요.
  • 의미 있는 오류 타입을 만들어 주세요.
enum InstallationError: Error {
case invalidURL
case networkFailure(Error)
case insufficientStorage
case deviceNotSupported
}

func installApp() throws -> App {
guard let url = URL(string: urlString) else {
throw InstallationError.invalidURL
}
// Implementation
}

Objective-C 오류 처리

  • NSError ** 패턴을 사용해 주세요.
  • 값을 설정하기 전에 포인터가 nil이 아닌지 확인해 주세요.
- (BOOL)installAppWithError:(NSError **)error {
if (someCondition) {
if (error) {
*error = [NSError errorWithDomain:SSErrorDomain
code:SSErrorCodeInvalidInput
userInfo:nil];
}
return NO;
}
return YES;
}

모범 사례

메모리 관리

  • Swift와 Objective-C 모두 ARC를 올바르게 활용해 주세요.
  • 순환 참조에 주의하고 weak, unowned를 적절히 사용해 주세요.
// ✅ 좋습니다
class AppInstaller {
weak var delegate: AppInstallerDelegate?

private lazy var networkManager: NetworkManager = {
let manager = NetworkManager()
manager.delegate = self // Self is strong reference, but manager doesn't retain self
return manager
}()
}

스레딩

  • UI 업데이트는 항상 메인 큐에서 해 주세요.
  • 백그라운드 작업에는 알맞은 큐를 사용해 주세요.
// ✅ 좋아요
DispatchQueue.global(qos: .userInitiated).async {
let result = self.processData()
DispatchQueue.main.async {
self.updateUI(with: result)
}
}

옵셔널 처리

  • 안전한 언래핑 기법을 사용해 주세요.
  • 조기 반환이 필요하면 guard를 선호해 주세요.
// ✅ 좋아요
guard let data = response.data,
let apps = try? JSONDecoder().decode([App].self, from: data) else {
completion(.failure(ParsingError.invalidResponse))
return
}

도구와 자동화

SwiftLint

SwiftLint를 사용하면 많은 포매팅 규칙을 자동으로 강제할 수 있어요.

# .swiftlint.yml
line_length: 120
function_body_length: 60
file_length: 400
type_body_length: 300

disabled_rules:
- trailing_whitespace

opt_in_rules:
- empty_count
- force_unwrapping

기억해 주세요. 이 가이드는 코드의 가독성과 유지 보수성을 높이기 위한 거예요. 헷갈릴 때는 명확성과 기존 코드베이스와의 일관성을 최우선으로 해 주세요.