Rails try try! &. 에대해 알아보자

Posted by negabaro kim on Friday, May 22, 2020 Tags: rails ruby   5 minute read

try란?

레시버가 nil이 아니라면 try의 파라메터에 지정된 메소드를 호출한다.

트와이스.try(:사나)

트와이스가 존재하면 사나를 호출함 존재하지 않으면 nil을 리턴

try를 쓰는 이유

코드의 가독성을 위해 사용한다.

@user.name라는 코드가 있고 @user가 nil일 경우 name이라는 메소드를 찾을 수 없으므로 NoMethodError가 발생하게된다

@user && @user.name

그래서 이런식으로 @user가 존재할경우 @user.name을 보게함으로 NoMethodError가 발생하는 부분을 해결했다. 그렇지만 @user를 2번 적어줘야되는게 dry하지 않고 가독성도 좋지않다.

이때 try를 사용하는데

@user.try(:name)

이런식으로 써주면 @user가 없을때 (:name) 부분은 실행되지 않고 nil을 리턴한다. @user를 두번써줄 필요도 없고 코드가 훨씬 깔끔해졌다.

try! 란?

역사적으로는 원래 try만 있었다가 try의 문제점을 해결하기 위해 try!가 나중에 나왔다.

그 문제는 try가 메소드의 존재여부를 무시하고 nil을 넘겨준다는 것이다.

아래 코드를 보자

트와이스.try(:서현)
> nil

예를들어 트와이스그룹에 서현이 없는데도 NoMethodError가 나지않고 애매모호하게 nil..하고 답변을 하는게 문제였던것

원래목적은 레시버의 데이터가 존재하냐 안하냐였는데

트와이스2.try(:서현)
> nil
트와이스.try(:서현)
> nil

둘다 nil을 리턴함으로 인해 레시버가 없는지 메소드가 없는지 구분이 불가능

try!를 사용하면 에러를 발생시켜 레시버가 없는 현상과의 구분을 시킬 수 있다.

트와이스.try!(:서현)
> NoMethodError: undefined method `call' for

에러가 안나는 try가 좋을 경우가 있을지 모르지만 어디까지 예상범위 안이어야 한다.

타이핑 미스라든지 뷰에 이상한 값을 표시해놓고 그것을 알아채는데 오래걸리는 문제가 있음 그러한 문제를 해결하기 위해 try!를 자주 사용한다.(특별한 이유가 없다면 try!나 후술할 &.를 사용하시길)

레시버값이 없으면 try와 동일하게 nil을 리턴함

트와이스2.try!(:서현)
> nil

&. == try!

try!대신 &.를 사용할 수 있다.

&.를 사용하면 코드가 짦아지긴 하는데 생소한 문법이다 보니 레일즈 경험이 많지 않은 개발자들이 읽기 힘들지도..

ruby2.3부터 생긴 기능인데 한국어로 뭐라하는줄 모르겠다. 일본어로는 ぼっち演算子(봇치연산자) 라고 부른다.

아래와 같이 사용한다.

oo&.decorate&.name
=> "xx"

이런게 짦으면 별 차이없이 느껴질 수 있는데 chain이 늘어나면 차이는 크다.

User.find_by(name: params[:name])&.hello&.hello2&.hello3&.hello4
User.find_by(name: params[:name]).try!(:hello).try!(:hello2).try!(:hello3).try!(:hello4)

또 하나 봇치연산자를 사용하면 좋은 이유는 ruby에서도 쓸 수있기 때문이다.

봇치연산자는 ruby 2.3.0에 추가된 기능인데 거기에 반해 try!는 ActiveSupport의 기능이다. 즉 try!는 레일즈에서 밖에 쓸 수 없다.

파라메터를 받는 메소드일 경우

메소드가 변수를 받을때 try의 2번째 이후 변수에 지정해준다

def xx_for_deubg(keke)
  pp "#{keke}..xx"
end
uu = User.find(1)
uu.try(:xx_for_deubg, 5)
"5..xx"

메소드가 블록을 받을 경우

(1..10).try(:select){|x| x.even?}
> => [2, 4, 6, 8, 10]

이하와 같이도 쓴다.

(1..10).try(:select, &:even?)
> => [2, 4, 6, 8, 10]

메모1

당연한 얘기지만 decorate를 호출후 decorate안에 name메소드가 없으므로 에러가 발생

nil.try(:decorate).name
NoMethodError: undefined method `name' for nil:NilClass

메모2

2개의 레시버를 거칠때는 try2개 써줘야됨.

oo.try(:decorate).try(:name)
=> "xx"

메모3

봇치는 일본어로 외톨이라는 뜻인데

&.가 방구석에서 쭈구렸을때 모습이랑 비슷해서 그런게 부른다고 한다.

image

메모4

ActiveModel에서 find한 값을 그대로 레시버로 쓰면 안됨

레일즈에서 아래와 같이 변수에 넘겨주지 않은 상태면 에러가 났다.

Organization.find(34343).try(:decorate)
ActiveRecord::RecordNotFound: Couldn't find Organization with 'id'=34343

변수에 넘겨준후 try를 실행하면 에러가 발생하지 않음.

org = Organization.find(34343)
org.try(:decorate)

find_by는 문제가 없다. find의 문제인듯

Organization.find_by(34343).try(:decorate)

reference:

https://pikawaka.com/rails/try
https://qiita.com/ngron/items/ec5f72639634949c126e