[Rails] create vs create!

Jinsu Kim·2023년 10월 14일
1

rails

목록 보기
7/7
post-thumbnail

코딩테스트를 보던 중 POST기능을 만들어 달라는 문제에서 아직 깊게 생각하지 못한 부분이 많다고 생각하여 좀 더 깊게 정리해보고자 작성했습니다!

먼저 create vs create!의 차이는?

create

def create
  if user.create(user_params)
    redirect_to root_path
  else
    render new
  end
end
  1. ActiveRecord메소드 create(new + save)를 읽어들입니다
  2. user_params에서 파라미터를 가저옵니다
  3. create의 save부분에서 validation을 체크합니다

→ 실패할 경우 false가 반환값이 됩니다

create!

def create
  @user = user.create!(user_params)
  redirect_to root_path
end

create!의 경우도 1,2,3 까지는 같습니다. 그러나 마지막 부분의 결과에서 차이가 있습니다.
1. ActiveRecord메소드 create(new + save)를 읽어들입니다
2. user_params에서 파라미터를 가저옵니다
3. create의 save부분에서 validation을 체크합니다

→ 성공할 경우 ActiveRecord오브젝트에 레코드를 실패할 경우 에러(예외)를 출력합니다

그래서 !가 뭐야? 🧐

Rails에서 !는 예외를 발생시킨다는 의미로 사용됩니다
예를 들어

  • save!
  • update!
  • destory!

변수 이름을 지을 때도 메소드에서 에러를 발생시크는 메소드의 경우 !를 마지막에 사용합니다(boolean이 return값이면?를 사용하는 것처럼)

# 예외값을 갖는 경우
def validate_user!
  raise StandardError if params[:name] == ''
rescue ActionController::BadRequest
  render_404
end

! 가없고 있고 없고의 차이점

save,create,updatesave!,create!,update!
ActiveRecord::RecordInvalid(validation)return false예외
ActiveRecord::RecordNotSaved(callback)return false예외
ActiveRecord::RecordNotUnique,ActiveRecord::StatementInvalid등예외예외

가볍게 정리하면 위에 표처럼 됩니다.
위에서 정리했던 내용과 조금 다른점은 NotUnique, StatementInvalid이 경우는 save의 경우에도 예외를 출력합니다

왜?🧐
ActiveRecord::RecordNotUnique의 경우, 데이터베이스가 고유성 제약 위반(Unique제약)을 발견한 경우 예외를 발생시키기 때문에 Rails쪽에서도 예외를 발생시킨다

ActiveRecord::StatementInvalid에러 경우도 RecordNotUnique와 비슷한 경우로 SQL구문에러가 발생한 경우에 해당한다.

DB관련에서 에러가 발생한 경우에는 Rails ! 관계 없이 예외를 발생시키는 것 같다

로그를 한번 확인해보자

pry(main)> User.create(group_id: 8)

TRANSACTION (2.0ms)
BEGIN
User Create (4.6ms)  INSERT INTO 생략  TRANSACTION (0.3ms)  
ROLLBACK

ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR:  
null value in column "컬럼" violates not-null constraint

그래서 Error handling을 어떤식으로 하면 좋을까?

!가 없는 경우

scaffold을 사용한 경우로 정리해보았습니다

def create
  @user = User.new(user_params)

  respond_to do |format|
    if @user.save
      format.html { redirect_to @user, notice: "User was successfully created." }
      format.json { render :show, status: :created, location: @user }
    else
      format.html { render :new, status: :unprocessable_entity }
      format.json { render json: @user.errors, status: :unprocessable_entity }
    end
  end
end

이 경우 개인적으로 생각한 장점・단점의 경우는 아래와 같다
장점

  • 간단한 저장 기능을 구현 하면 가독성 좋게 코드를 구현할 수 있다(트랜잭션을 사용하지 않거나 하는 경우)

단점

  • 상세하게 에러 핸들링을 해야 하는 경우에는 할 수 가 없다(자세한 내용은 아래에서)

!가 있는 경우

def create
  return render status: :conflict if User.exists?(id: task_params[:id])

  task = User.create!(user_params)

  render json: user
rescue ActiveRecord::RecordInvalid
  Rails.logger.error('RecordInvalidError 00정보가 없습니다')
  render status: :bad_request
rescue StandardError
  Rails.logger.error('StandardError')
  render status: :internal_server_error
end

!가 없는 경우의 장점, 단점이 반대로 될 것 같다.
장점

  • 코드를 보며 확인할 수 있듯이 상세하게 에러 핸들링이 가능하다. API를 사용하는 유저에게 명확한 스테이터스를 제공해야 하거나 에러 메세지를 제공해야 하는경우에 좋을 것 같다
  • rescue_form을 사용하여 에러를 공통으로 사용할 수 있다. 이부분에 대해서는 다른 글에서 정리해 보려고 한다

단점

  • 에러 핸들링을 잘 안해노면 에러가 발생하여 동작이 멈추게 된다(적절한 에러 핸들링을 하면 문제가 될일은 없을 것 같다. 적어도 StandardError를 해논다면...)

📃 비고
잘 나와있는 사이트가 있어서 공유합니다! 일본사이트인대 보는대는 딱히 문제가 없을 겁니다!
Rails HTTP Status의 심볼이 잘 나와있는 사이트(일본어)

그 외 transaction의 경우

transaction를 롤백 시키기 위해서는 저장하는 메소드에 !를 사용할 필요가 있습니다. save의 경우 실패해도 false를 리턴하기 때문에 Transaction에서는 메소드가 문제가 없다고 판단하고 진행하기 때문입니다

ActiveRecord::Base.transaction do
  user1.save!
  user2.save # 에러 발생!
  user3.save!
end

위에 코드에서 user2에서 에러가 발생해도 트랜잭션은 동작하지 않습니다!
이유는 위에 작성했던 것 처럼 예외가 발생하지 않기 때문입니다.
따라서 트랙잭션을 사용하는 경우에는 !가 필요한점 꼭 주의해주세요!

결론

정리 해보고 생각했던점은 무의식적으로 API를 작성하고 있었지만 중요한 것은 어떤 정보를 유저에게 보여주고 어떻게 에러 핸들링을 해 나갈지였던 것 같습니다.

정리를 해보며서 느낀점은 저희 회사에는 간단한 설정화면의 저장 기능이 아닌 이상 대부분 !를 사용하여 에러 핸들링 하는게 많은 것 같습니다.

상황에 맞게 적절하게 코드를 작성하면 되고, 적절하게 메소드를 사용해나가면 좋을 것 같습니다!

틀린 부분이나 뭔가 추가 정보 같은 부분은 코멘트 환영입니다!

profile
Ruby와 js로 첫 커리어를 시작하였고 4년차 엔진니어입니다! 현재 Rails, vim에 관심이 많습니다!

0개의 댓글