R Academy · 강의

stop()과 warning()으로 견고한 함수 작성하기

자신이 작성한 함수 내부에서 사용자 지정 조건을 알립니다.

레슨 4/413개 단계

stop()과 warning()으로 견고한 함수 작성하기은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 R Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

입력 검증이 중요한 이유

방어적인 함수는 입력을 확인하고, 잘못된 결과를 조용히 생성하는 대신 유용한 오류 정보를 포함해 명확하게 실패합니다. 경계에서 잘못된 입력을 포착하는 함수가 잘못된 데이터를 파이프라인 전체로 전달하는 함수보다 디버깅하기 훨씬 쉽습니다.

# Without validation: wrong result, no error
bad_mean <- function(x) sum(x) / length(x)
bad_mean('hello')   # no error, just NA

# With validation: clear error
good_mean <- function(x) {
  if (!is.numeric(x)) stop('x must be numeric')
  sum(x) / length(x)
}
tryCatch(good_mean('hello'), error = function(e) cat(e$message, '\n'))

유용한 오류 메시지

stop('bad input')과 같은 일반적인 오류는 디버깅을 어렵게 만듭니다. stop() 내부에서 paste0() 또는 sprintf()를 사용해 실제 값과 예상 유형을 메시지에 포함하십시오. 이렇게 하면 파이프라인에서 오류를 추적할 때 시간을 절약할 수 있습니다.

check_numeric <- function(x, arg_name = 'x') {
  if (!is.numeric(x)) {
    stop(sprintf(
      "'%s' must be numeric, but got class '%s'",
      arg_name, class(x)
    ))
  }
}
tryCatch(check_numeric('abc', 'score'),
  error = function(e) cat(e$message, '\n'))

길이와 범위 검증

일반적인 검증에는 벡터가 비어 있지 않은지 확인하는 것(length(x) == 0), 스칼라가 범위 안에 있는지 확인하는 것, 데이터 프레임에 예상 열이 있는지 확인하는 것이 있습니다. 각 확인에는 그에 맞는 오류 메시지를 별도로 작성해야 합니다.

validate_prob <- function(p) {
  if (!is.numeric(p)) stop(paste0('p must be numeric, got: ', class(p)))
  if (length(p) != 1) stop(paste0('p must be length 1, got: ', length(p)))
  if (p < 0 || p > 1) stop(paste0('p must be in [0,1], got: ', p))
  p
}
cat(validate_prob(0.7), '\n')
tryCatch(validate_prob(1.5), error = function(e) cat(e$message, '\n'))

치명적이지 않은 문제에 warning() 사용

함수가 여전히 합리적인 결과를 반환할 수 있지만 호출자가 특이한 상황을 알아야 할 때 warning()을 사용하십시오. 예를 들어 값을 강제 변환했거나, 기본값을 대입했거나, 경계에 가까운 입력을 감지한 경우입니다.

clamp <- function(x, lo, hi) {
  if (any(x < lo)) warning(paste0(sum(x < lo), ' value(s) below lo, clamped'))
  if (any(x > hi)) warning(paste0(sum(x > hi), ' value(s) above hi, clamped'))
  pmax(lo, pmin(hi, x))
}
result <- clamp(c(2, -5, 7, 100, 4), 0, 10)
cat('Result:', result, '\n')

stop()의 call. = FALSE

기본적으로 stop()은 메시지 앞에 Error in funcname(...):을 붙입니다. 호출 컨텍스트가 분명하거나 함수가 사용자 대상 유틸리티인 경우 call. = FALSE를 전달해 이 접두사를 억제하십시오.

# With call. = TRUE (default): shows calling function
f1 <- function(x) stop('bad input')
tryCatch(f1(1), error = function(e) cat(conditionMessage(e), '\n'))

# With call. = FALSE: cleaner message
f2 <- function(x) stop('bad input', call. = FALSE)
tryCatch(f2(1), error = function(e) cat(conditionMessage(e), '\n'))

stop()용 사용자 지정 조건 클래스

structure()를 사용해 사용자 지정 오류 클래스를 만들면 호출자가 모든 오류를 포착하지 않고 해당 함수의 오류만 포착할 수 있습니다. 이는 패키지 수준의 오류를 작성하는 전문적인 방법입니다.

value_error <- function(msg, call = sys.call(-1)) {
  structure(
    class = c('value_error', 'error', 'condition'),
    list(message = msg, call = call)
  )
}
check_positive <- function(x) {
  if (x <= 0) stop(value_error(paste0('Expected positive, got: ', x)))
  x
}
tryCatch(check_positive(-3),
  value_error = function(e) cat('ValueError:', e$message, '\n'),
  error        = function(e) cat('Other error:', e$message, '\n'))

데이터 프레임 열 확인

함수가 데이터 프레임을 예상한다면 사용하기 전에 필요한 열이 존재하는지 검증하십시오. 열 이름을 확인하려면 %in%을 사용하고, 누락된 열을 보고하려면 setdiff()를 사용하십시오.

require_cols <- function(df, cols) {
  missing <- setdiff(cols, names(df))
  if (length(missing) > 0) {
    stop(paste0('Missing columns: ', paste(missing, collapse = ', ')))
  }
  invisible(df)
}
df <- data.frame(x = 1:3, y = 4:6)
tryCatch(
  require_cols(df, c('x', 'z', 'w')),
  error = function(e) cat(e$message, '\n')
)

간결한 단정에 stopifnot() 사용

stopifnot(condition1, condition2, ...)는 조건 중 하나라도 FALSE이면 오류를 발생시킵니다. 함수 상단에서 사전 조건을 단정할 때 별도의 if (!...) stop(...) 블록을 작성하지 않아도 되는 간결한 방법입니다.

compute_area <- function(width, height) {
  stopifnot(
    is.numeric(width),
    is.numeric(height),
    width > 0,
    height > 0
  )
  width * height
}
cat(compute_area(5, 3), '\n')
tryCatch(compute_area(-1, 3), error = function(e) cat(e$message, '\n'))

더 나은 메시지를 위한 이름 있는 stopifnot()

R 4.0 이상에서는 stopifnot()의 이름 있는 표현식이 자동 생성 메시지를 사용자 지정 설명으로 바꿉니다. 표현식 텍스트를 이름으로 사용하면 실패 원인을 알려 주는 유용한 메시지를 만들 수 있습니다.

validate_age <- function(age) {
  stopifnot(
    'age must be numeric'    = is.numeric(age),
    'age must be positive'   = age > 0,
    'age must be under 150'  = age < 150
  )
  invisible(age)
}
tryCatch(validate_age(-5), error = function(e) cat(e$message, '\n'))
tryCatch(validate_age('x'), error = function(e) cat(e$message, '\n'))

검증과 비즈니스 로직 결합

잘 구성된 함수는 검증과 로직을 분리합니다. 먼저 상단에서 검증한 다음 작업을 수행하십시오. 이렇게 하면 함수를 읽기 쉬워지고 계산을 시작하기 전에 오류를 감지할 수 있습니다.

discount_price <- function(price, pct) {
  if (!is.numeric(price) || price <= 0)
    stop(paste0('price must be positive numeric, got: ', price))
  if (!is.numeric(pct) || pct < 0 || pct > 100)
    stop(paste0('pct must be in [0,100], got: ', pct))
  if (pct > 50) warning('discount > 50% is unusual')
  price * (1 - pct / 100)
}
cat(discount_price(100, 20), '\n')
cat(suppressWarnings(discount_price(100, 60)), '\n')

유틸리티 함수 내부의 tryCatch

때로는 유틸리티 함수가 오류를 내부에서 처리하고 오류를 전달하는 대신 대체값을 반환해야 합니다. 핵심 로직을 tryCatch()로 감싸고 실패 시 NA 또는 NULL과 같이 미리 정한 표시값을 반환하십시오.

safe_log <- function(x) {
  tryCatch({
    if (!is.numeric(x)) stop('not numeric')
    if (x <= 0) stop('must be positive')
    log(x)
  }, error = function(e) {
    warning(paste0('safe_log failed for x=', x, ': ', e$message))
    NA_real_
  })
}
results <- sapply(list(10, -1, 'a', 100), safe_log)
cat(results, '\n')

빠른 확인

stop()에 전달할 때 call. = FALSE는 어떤 작업을 수행합니까?

안정적인 함수: 핵심 정리

안정적인 함수 작성의 핵심 정리:

  • 상단에서 입력을 검증하고 유용한 stop() 메시지와 함께 명확하게 실패시키십시오.
  • 오류 메시지에 실제 값을 포함하십시오: paste0('Expected numeric, got: ', class(x))
  • 계산을 계속할 수 있는 치명적이지 않은 문제에는 warning()을 사용하십시오.
  • call. = FALSE는 오류 메시지에서 호출 함수 접두사를 억제합니다.
  • 간결한 단정에는 stopifnot()을 사용하고, 사용자 지정 메시지에는 이름 있는 형식을 사용하십시오(R 4.0 이상).
  • 사용자 지정 조건 클래스를 사용하면 호출자가 오류를 선택적으로 포착할 수 있습니다.
robust_divide <- function(x, y) {
  stopifnot('x must be numeric' = is.numeric(x),
            'y must be numeric' = is.numeric(y))
  if (y == 0) stop('Division by zero', call. = FALSE)
  if (abs(y) < 1e-10) warning('y is very small; result may be inaccurate')
  x / y
}
cat(robust_divide(10, 2), '\n')
tryCatch(robust_divide(10, 0), error = function(e) cat(e$message, '\n'))
무료로 시작

AI 튜터와 함께 R을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
43
레슨
159

자주 묻는 질문

“stop()과 warning()으로 견고한 함수 작성하기” 강의는 무료인가요?

네 — “stop()과 warning()으로 견고한 함수 작성하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“stop()과 warning()으로 견고한 함수 작성하기”에서 뭘 배우나요?

자신이 작성한 함수 내부에서 사용자 지정 조건을 알립니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

R Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“stop()과 warning()으로 견고한 함수 작성하기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. R의 오류, 경고, 메시지
  2. 오류 복구를 위한 tryCatch()
  3. withCallingHandlers()와 재시작
  4. stop()과 warning()으로 견고한 함수 작성하기
← R Academy(으)로 돌아가기