한가지 작업이란?

우리는 흔히 함수는 하나의 작업만 해야 한다고 합니다. 그런데 하나의 작업이란 어떤것을 뜻하는지 정확히 알지 못해서 많이 햇갈렸습니다. 그렇다면 if문이나 for문 이런것도 각각 하나의 함수로 만들어줘야 하나? 함수 안에 또다른 함수를 이용하는것은 여러가지 작업을 하는것이니 이런식으로 구현하면 안되나 등등 고민을 많이 했습니다.

클린 코드(로버트 C 마틴)에서는 한가지 작업이란 다음과 같이 정의합니다.

추상화 수준이 하나인 단계만 수행한다면 그 함수는 한가지 작업만 한다.

처음 들었을때 무슨소리인가 하겠지만, 예제를 하나 보면 이해하기 쉽습니다.

 

로그인 기능을 구현한다고 가정해보겠습니다. 저희는 버튼이 클릭하면 로그인 기능을 담당하는 객체의 로그인 함수를 통해 로그인을 진행합니다. 

class LoginObject {

    func login() {
        
    }
}

이때 로그인은 토큰을 받아서 인증을 합니다. 핸드폰에 토큰이 있는 경우, 서버에 토큰을 보내 유효한지 인증을 하고, 유효하지 않거나 토큰이 없을 경우 서버에 토큰을 받습니다.

여기서 또 추상화가 일어납니다.

  • 핸드폰에 토큰이 있는지 없는지 확인
  • 토큰이 있는경우 해당 토큰을 서버에 보내 인증
  • 토큰이 없는 경우 서버에서 토큰을 받아오기
class LoginObject {

    func login() {
        if checkOwnToken() {
            requestTokenIsValid()
        } else {
            requestNewToken()
        }
    }
    
    func checkOwnToken()->Bool {
        //구현
    }
    
    func requestTokenIsValid() {
        
    }
    
    func requestNewToken() {
        //구현
    }
}

이제 위 세가지에서 또 추상화를 할 수 있는지 살펴봅시다. checkOwnToken은 핸드폰에 토큰이 있는지 없는지 검사를 합니다. 여기서 더이상 추상화를 할 수 없을것 같군요. 

requestNewToken 역시 서버에 토큰을 요청을 하는 작업만을 담당합니다. 여기서 더이상 추상화를 할 수 없을것 같네요.

requestTokenIsValid() 함수는 서버에 토큰을 보내 토큰이 유효하면 인증이 완료되어 다음 화면으로 넘어가고 유효하지 않으면 새로운 토큰을 요청합니다. 이 역시 추상화가 가능할것 같네요.

  • 서버에 토큰을 보낸다.
  • 토큰이 유효한 경우 사용자 정보와 함께 다음 화면으로 이동
  • 유효하지 않은경우 토큰을 받아온다

이를 소스코드로 구현해보면 

class LoginObject {

    func login() {
        if checkOwnToken() {
            certifyToken()
        } else {
            requestNewToken()
        }
    }
    
    func checkOwnToken()->Bool {
        //구현
    }
    
    func certifyToken(){
        requestTokenIsValid { [weak self] result in
            switch result {
            case .success(let data):
                self?.passUserInfoMainView()
            case .failure(let error):
                self?.requestNewToken()
            }
        }
    }
    
    func passUserInfoMainView() {
    	//유저 정보를 다음 화면으로 넘김
    	//구현
    }
    
    func requestTokenIsValid(completion: @escaping(Result<Data,Error>)->Void) {
    	//서버에 토큰을 보내 유효한 토큰인지 아닌지 확인
       //구현
    }
    
    func requestNewToken() {
    	//서버로부터 새로운 토큰 요청
        //구현
    }
}

이런식으로 구현을 할 수 있습니다. 이제 각 함수는 추상화 수준이 하나인 단계만 수행합니다. login은 로그인이라는 추상화된 작업을, certifyToken은 토큰의 유효성 검사만 진행합니다. 또 함수를 보시면 추상화 수준이 같은 함수끼리만 모여있는것을 볼 수 있습니다. login 함수 안에는 추상화 수준이 높은 함수만 모여있고, 그 안에 있는 함수들을 보면 추상화 수준이 낮은 함수끼리만 모여 있습니다. 이처럼 같은 수준의 함수들만 공존해야지, 다른 수준의 함수들이 공존하게 되면, 가독성이 떨어지게 되어버립니다. 따라서 함수를 짤때 추상화의 수준역시 고려해야 할것 같습니다.

'클린 코드' 카테고리의 다른 글

객체와 자료구조  (0) 2022.09.03
의미있는 이름 정하기  (0) 2022.08.30