최근에 키워드 인수(keyword arguments)를 언제 사용해야 하는지에 대한 의견을 묻는 이메일을 받았습니다.
그 답변을 Ruby 메서드 인수에 대한 완전한 아티클로 확장해서 여러분 모두에게 도움이 되도록 정리해 보려고 합니다!
Ruby는 메서드 인수와 관련해 매우 유연한 언어입니다.
모든 종류의 인수를 지원합니다.
기본적인 필수 인수(required arguments)부터 선택적 인수(optional arguments), 그리고 키워드(이름 기반) 인수까지 말이죠.
이 글에서는 각 인수 유형의 차이점과 상황에 따라 어떤 인수를 사용해야 하는지 배우게 됩니다.
올바른 종류의 인수를 사용하면 코드를 읽기 쉽고 다루기 편하게 만들 수 있습니다.
확실하지 않다면 표준 인수를 사용하세요
필수 인수는 별도 설정 없이 기본적으로 적용되는 방식입니다.
예시를 보겠습니다:
def write(file, data, mode)
end
write("cats.txt", "cats are cool!", "w")필요한 개수만큼 정확히 인수를 전달하지 않으면 익숙한 에러 메시지를 만나게 됩니다:
write("shopping_list.txt", "bacon")
ArgumentError: wrong number of arguments (2 for 3)즉, 메서드에 2개의 인수를 전달했지만 실제로는 3개가 필요하다는 뜻입니다!
선택적 인수로 유연한 메서드 만들기
기본값(default value)을 제공해서 일부 인수를 선택적으로 만들고 싶을 때가 있습니다.
예를 들면:
def write(file, data, mode = "w") end
이제 write를 2개의 인수로 호출할 수 있으며, 이 경우 mode는 기본값("w")으로 설정됩니다. 반대로 3개의 인수를 전달하면 기본값을 덮어써서 다른 결과를 얻을 수 있습니다.
이런 기본값은 호출 시점의 작업을 줄여주면서도, 필요할 때 값을 변경할 수 있는 옵션은 그대로 유지해 줍니다.
키워드 인수로 명확성 높이기
필수 인수와 기본값 인수의 단점 중 하나는 순서에 의존한다는 점입니다.
즉, 인수를 반드시 정해진 순서대로 전달해야 합니다.
잘못된 순서로 전달하면 예상치 못한 결과가 발생합니다!
예를 들어 write 메서드에서는 먼저 file, 그다음 data, 마지막으로 mode 순서로 호출해야 합니다.
def write(file, data, mode) end
키워드 인수를 사용하면 인수의 순서를 자유롭게 바꿀 수 있습니다.
예시입니다:
def write(file:, data:, mode: "ascii") end
이제 메서드를 다른 순서로 호출할 수 있습니다:
write(data: 123, file: "test.txt")
하지만 더 중요한 점은, 메서드를 호출할 때 각 인수의 의미를 명확하게 알 수 있다는 것입니다.
키워드 인수를 자주 사용하지는 않지만, 메서드 호출에 명확성을 더하고 싶을 때 유용합니다.
예를 들어 Point 클래스가 있다고 가정해 봅시다.
이렇게 작성할 수 있습니다:
class Point
def initialize(x:, y:)
@x, @y = x, y
end
end
point = Point.new(x: 10, y: 20)이렇게 하면 두 숫자(10과 20) 중 어느 것이 x 값이고 어느 것이 y 값인지 혼동될 여지가 전혀 없습니다.
코드를 작성할 때의 주요 목표 중 하나는 바로 혼란과 오해를 최대한 피하는 것입니다.
키워드 인수가 그 목표 달성에 도움이 된다면 과감히 사용하세요.
스스로에게 물어보세요:
"키워드 인수를 추가하면 코드가 더 이해하기 쉬워질까, 아니면 불필요한 작업과 복잡함만 늘어날까?"
키워드 인수의 또 다른 장점은 누락된 인수를 매우 명확하게 알려준다는 점입니다.
예시:
point = Point.new # missing keywords: x, y
키워드 인수는 일반 인수와 조합해서 사용할 수도 있습니다.
Ruby 내장 메서드에서 관찰되는 한 가지 패턴은, 새 버전일수록 새로운 선택적 인수를 키워드 인수 형태로 추가하는 경향이 있다는 점입니다.
대표적인 예로 Ruby 2.4에서 도입된 Float#round, Kernel#clone, String#lines의 키워드 인수를 들 수 있습니다.
가변 인수로 필요한 만큼 값 받기
개수 제한 없이 값을 무한정 받고 싶다면 어떻게 해야 할까요?
이럴 때 가변 인수(variable arguments)를 사용합니다:
def print_all(*args) end print_all(1, 2, 3, 4, 5)
이렇게 하면 인수를 몇 개든(심지어 0개든) 자유롭게 전달하며 메서드를 호출할 수 있습니다. 가변 인수는 전달된 모든 값을 담는 배열이 됩니다.
다른 종류의 인수와 조합할 수도 있습니다.
예시:
def print_all(title, *chapters) end
첫 번째 인수는 title로, 나머지는 chapters 배열로 처리됩니다. 가변 인수는 반드시 모든 필수·선택적 인수 뒤에, 그리고 키워드 인수 앞에 위치해야 한다는 점에 유의하세요.
올바른 순서 지키기
여러 인수 유형을 조합할 때 문법 오류를 피하려면 다음 순서를 지켜야 합니다:
required -> optional -> variable -> keyword
모든 인수 유형을 한 번에 보여주는 메서드입니다:
def testing(a, b = 1, *c, d: 1, **x)
p a,b,c,d,x
end
testing('a', 'b', 'c', 'd', 'e', d: 2, x: 1)**x는 가변 인수와 같은 개념이지만 키워드 인수용입니다. 배열 대신 해시(hash)로 전달받게 됩니다.
모든 것을 받아들이는 인수(Catch-All Argument)
드물지만 다음과 같은 코드를 볼 수 있습니다:
def print_all(*) end
이는 메서드가 어떤 인수든 받아들이지만, 그 인수들로 아무 작업도 하지 않겠다는 의미입니다. 블록 안에서 밑줄 문자(_)를 사용해 사용하지 않는 인수를 표시하는 것과 비슷합니다.
실용적인 활용 예로 super 키워드와 함께 사용하는 경우가 있습니다:
class Food
def nutrition(vitamins, minerals)
puts vitamins
puts minerals
end
end
class Bacon < Food
def nutrition(*)
super
end
end
bacon = Bacon.new
bacon.nutrition("B6", "Iron")이렇게 하면 Food의 nutrition 메서드가 인수 목록을 변경하더라도 Bacon 쪽 인수를 수정할 필요가 없습니다.
마무리
지금까지 Ruby의 강력한 메서드 인수와 활용 방법을 살펴봤습니다! 필수 인수로 시작하고, 선택적 인수로 유연성을 더하며, 키워드 인수로 명확성을 높이는 것이 좋은 접근입니다.
이 글이 도움이 되었기를 바랍니다!
유익했다면 Twitter에 공유해서 더 많은 사람들이 볼 수 있게 해주세요 🙂