유효하지 않은 사용자 입력을 어떻게 처리해야합니까?


12

나는이 문제에 대해 잠시 동안 생각해 왔으며 다른 개발자의 의견이 궁금합니다.

나는 매우 방어적인 스타일의 프로그래밍을하는 경향이 있습니다. 내 일반적인 블록 또는 방법은 다음과 같습니다.

T foo(par1, par2, par3, ...)
{
    // Check that all parameters are correct, return undefined (null)
    // or throw exception if this is not the case.

    // Compute and (possibly) return result.
}

또한 계산 중에 포인터를 참조 해제하기 전에 모든 포인터를 확인합니다. 내 생각은 버그가 있고 NULL 포인터가 어딘가에 나타나야한다면 내 프로그램이 이것을 잘 처리하고 계산을 계속 거부해야한다는 것입니다. 물론 로그 또는 다른 메커니즘의 오류 메시지로 문제를 알릴 수 있습니다.

좀 더 추상적 인 방식으로 표현하기 위해 내 접근 방식은

if all input is OK --> compute result
else               --> do not compute result, notify problem

다른 개발자들 중 일부 동료들은 다른 전략을 사용합니다. 예를 들어, 포인터를 확인하지 않습니다. 그들은 코드 조각에 정확한 입력이 주어져야하며 입력이 잘못되었을 때 발생하는 일에 대해 책임을지지 않아야한다고 가정합니다. 또한 NULL 포인터 예외가 프로그램과 충돌하면 테스트 중에 버그가 더 쉽게 발견되고 수정 될 가능성이 높아집니다.

이것에 대한 나의 대답은 일반적입니다. 그러나 테스트 중에 버그가 발견되지 않고 고객이 이미 제품을 사용하고있을 때 나타납니다? 버그 자체를 나타내는 바람직한 방법은 무엇입니까? 특정 작업을 수행하지는 않지만 계속 작동 할 수있는 프로그램이거나 충돌하여 다시 시작해야하는 프로그램이어야합니까?

요약

잘못된 입력을 처리하는 두 가지 방법 중 어느 것이 좋습니까?

Inconsistent input --> no action + notification

또는

Inconsistent input --> undefined behaviour or crash

편집하다

답변과 제안에 감사드립니다. 나도 계약으로 디자인의 팬입니다. 그러나 내 메서드를 호출하는 코드를 작성한 사람을 신뢰하더라도 (아마도 나 자신 일 수도 있음) 여전히 버그가있어 입력이 잘못 될 수 있습니다. 그래서 내 접근법은 메소드가 올바른 입력을 전달 받았다고 가정하지 않는 것입니다.

또한 메커니즘을 사용하여 문제를 포착하고 알려줍니다. 개발 시스템에서는 예를 들어 대화 상자를 열어 사용자에게 알립니다. 프로덕션 시스템에서는 일부 정보를 로그에 씁니다. 추가 검사를 수행하면 성능 문제가 발생할 수 있다고 생각하지 않습니다. 어설 션이 프로덕션 시스템에서 꺼져 있으면 어설 션이 충분한 지 확실하지 않습니다. 테스트 중에 발생하지 않은 프로덕션에서 일부 상황이 발생할 수 있습니다.

어쨌든 많은 사람들이 반대의 접근법을 따르는 것에 정말 놀랐습니다. 테스트하는 동안 버그를 찾기가 더 쉬워 질 것이기 때문에 애플리케이션이 "고의적"으로 충돌 할 수있었습니다.


항상 방어 적으로 코딩하십시오. 결국 성능상의 이유로 릴리스 모드에서 일부 테스트를 사용하지 않도록 스위치를 설정할 수 있습니다.
deadalnix

오늘은 누락 된 NULL 포인터 검사와 관련된 버그를 수정했습니다. 응용 프로그램 로그 아웃 중에 일부 개체가 생성되었으며 생성자가 getter를 사용하여 더 이상 존재하지 않는 다른 개체에 액세스했습니다. 그 시점에서 객체를 만들지 않았습니다. 다른 버그로 인해 생성되었습니다 : 로그 아웃 중 일부 타이머가 중지되지 않았습니다-> 신호가 전송되었습니다-> 수신자가 객체를 만들려고했습니다-> 생성자가 쿼리하여 다른 객체를 사용했습니다-> NULL 포인터-> 충돌 ). 그런 불쾌한 상황이 내 응용 프로그램과 충돌하는 것을 정말로 좋아하지 않습니다.
조르지오

1
수리 규칙 : 실패해야 할 경우, 시끄럽고 가능한 빨리 실패하십시오.
deadalnix

"수리 규칙 : 실패해야 할 경우, 시끄럽고 가능한 빨리 실패하십시오.": 모든 Windows BSOD가이 규칙의 적용이라고 생각합니다. :-)
Giorgio

답변:


8

맞습니다. 편집증하십시오. 자신의 코드라도 다른 코드를 신뢰하지 마십시오. 당신은 일을 잊고, 변경하고, 코드를 발전시킵니다. 외부 코드를 신뢰하지 마십시오.

위에서 좋은 지적을했습니다 : 입력이 유효하지 않지만 프로그램이 충돌하지 않으면 어떻게됩니까? 그런 다음 데이터베이스에 쓰레기가 생기고 오류가 발생합니다.

숫자 (예 : 달러 또는 단위 수)를 요청하면 "1e9"를 입력하고 코드의 기능을 확인합니다. 일어날 수 있습니다.

40 년 전 UCBerkeley에서 컴퓨터 과학 학사 학위를 취득한 결과 좋은 프로그램은 50 % 오류 처리라고 들었습니다. 편집증하십시오.


그렇습니다, IMHO 이것은 편집증이 특징이고 문제가 아닌 몇 가지 상황 중 하나입니다.
조르지오

"입력이 유효하지 않지만 프로그램이 충돌하지 않는 경우 어떻게해야합니까? 그러면 데이터베이스에 가비지가 발생하여 오류가 발생합니다.": 충돌 대신 프로그램이 작업 수행을 거부하고 정의되지 않은 결과를 반환 할 수 있습니다. 정의되지 않음은 계산을 통해 전파되며 가비지가 생성되지 않습니다. 그러나이를 달성하기 위해 프로그램이 충돌 할 필요는 없습니다.
조르지오

예, 그러나-요점은 프로그램이 유효하지 않은 입력을 감지하고 대처해야한다는 것입니다. 입력을 확인하지 않으면 시스템으로 들어가고 나중에 문제가 발생합니다. 심지어 충돌보다 더 좋습니다!
Andy Canfield

나는 당신에게 전적으로 동의합니다. 일반적인 방법이나 기능은 입력 데이터가 올바른지 확인하기 위해 일련의 검사로 시작합니다.
Giorgio

오늘 저는 "모든 것을 확인하고 아무것도 믿지 않는"전략이 종종 좋은 아이디어라는 확인을 다시 받았습니다. 내 동료가 검사 누락으로 인해 NULL 포인터 예외가 발생했습니다. 그 문맥에서 일부 데이터가로드되지 않았기 때문에 NULL 포인터를 갖는 것이 옳았으며 포인터를 확인하고 NULL 일 때 아무것도하지 않는 것이 옳았습니다. :-)
Giorgio

7

당신은 이미 올바른 생각을 가지고 있습니다

잘못된 입력을 처리하는 두 가지 방법 중 어느 것이 좋습니까?

일관되지 않은 입력-> 동작 없음 + 알림

또는 더 나은

일관되지 않은 입력-> 적절하게 처리 된 조치

프로그래밍에 쿠키 커터 접근 방식을 실제로 사용할 수는 없지만 의식적인 선택보다는 습관에서 벗어나는 공식적인 디자인으로 끝납니다.

실용주의와 성미 독단 법.

스티브 맥코넬 (Steve McConnell)

Steve McConnell 은 방어 프로그래밍에 관한 책 ( Code Complete )을 거의 썼습니다. 이것은 입력을 항상 검증해야한다는 조언 중 하나였습니다.

Steve가 이것을 언급했다면 기억이 나지 않지만 비 개인적 인 메소드와 함수 및 필요한 것으로 간주되는 다른 메소드와 함수에 대해이 작업을 수행하는 것을 고려해야 합니다.


2
공개 대신에, 모든 비 개인적 방법은 액세스 제한 개념을 보호, 공유 또는 개념이없는 언어를 방어 적으로 다루는 것이 좋습니다 (모든 것이 공개, 암시 적으로).
JustinC

3

특히 언어, 코드 유형 및 코드가 들어갈 수있는 제품 유형을 지정하지 않으면 "올바른"대답은 없습니다. 치다:

  • 언어 문제. Objective-C에서는 종종 nil로 메시지를 보내는 것이 좋습니다. 아무 일도 일어나지 않지만 프로그램도 충돌하지 않습니다. Java에는 명시적인 포인터가 없으므로 nil 포인터는 큰 관심사가 아닙니다. C에서는 좀 더 조심해야합니다.

  • 편집증이된다는 것은 불합리하고 보증되지 않는 의심이나 불신입니다. 그것은 사람들보다 소프트웨어에 더 좋지 않을 것입니다.

  • 우려 수준은 코드의 위험 수준 및 나타나는 문제를 식별하기가 어려울 수있는 수준에 상응해야합니다. 최악의 경우 어떻게됩니까? 사용자가 프로그램을 다시 시작한 후 중단 된 지점에서 계속 하시겠습니까? 회사는 수백만 달러를 잃는가?

  • 항상 잘못된 입력을 식별 할 수는 없습니다. 종교적으로 포인터를 nil과 비교할 수는 있지만 2 ^ 32 개의 가능한 값 하나만 잡습니다 . 거의 모든 값이 나쁩니다.

  • 오류를 처리하기위한 다양한 메커니즘이 있습니다. 다시 말하지만, 언어에 어느 정도 의존합니다. 어설 션 매크로, 조건문, 단위 테스트, 예외 처리, 신중한 디자인 및 기타 기술을 사용할 수 있습니다. 그들 중 누구도 절대 안전하지 않으며 모든 상황에 적합한 것은 없습니다.

따라서 대부분 책임을지고 싶은 곳으로 귀결됩니다. 다른 사람이 사용하기 위해 라이브러리를 작성하는 경우 입력에 대해 합리적으로 최대한주의를 기울이고 가능한 경우 유용한 오류를 발생시키기 위해 최선을 다하고 싶을 것입니다. 자신의 개인 함수와 메소드에서 어설 션을 사용하여 어리석은 실수를 잡을 수 있지만 그렇지 않으면 쓰레기를 전달하지 않도록 발신자 (당신 자신)에게 책임을 질 수 있습니다.


+1-좋은 답변입니다. 저의 주요 관심사는 잘못된 입력으로 인해 프로덕션 시스템에 문제가 발생할 수 있다는 것입니다 (어떤 일을하기에는 너무 늦었을 때). 물론 이러한 문제가 사용자에게 발생할 수있는 피해에 따라 다르다고 말하는 것이 전적으로 옳다고 생각합니다.
Giorgio

언어가 큰 역할을합니다. PHP에서는 메소드 코드의 절반이 변수의 유형을 확인하고 적절한 조치를 취합니다. Java에서 메소드가 int를 허용하면 다른 것을 전달할 수 없으므로 메소드가 더 명확 해집니다.
chap

1

예외 발생과 같은 알림이 있어야합니다. 사용자가 작성한 코드를 잘못 사용하여 의도하지 않은 작업에 사용하려고 시도 할 수있는 다른 코더의 입력으로 유효하지 않거나 오류가 발생합니다. 이것은 오류를 추적하는 데 매우 유용하지만 단순히 null을 반환하면 결과를 사용하고 다른 코드에서 예외를 얻을 때까지 코드가 계속 진행됩니다.

특정 코드의 범위를 벗어난 다른 코드 (아마도 데이터베이스 업데이트 실패)를 호출하는 동안 코드에 오류가 발생하면 실제로 코드를 제어 할 수 없으며 유일한 방법은 당신은 (당신이 호출 한 코드로 말한 것만 알고 있습니다). 특정 입력이 필연적으로 그러한 결과를 초래할 것이라는 것을 알고 있다면 코드를 실행하는 것을 귀찮게 할 수 없으며 어떤 입력이 유효하지 않은지와 그 이유를 설명하는 예외를 던질 수는 없습니다.

더 많은 최종 사용자 관련 메모에서는 누구나 이해할 수 있도록 설명 적이면서 간단한 것을 반환하는 것이 가장 좋습니다. 고객이 전화하여 "프로그램 충돌"이라고 표시하면 문제가 발생한 원인과 원인을 추적하고 문제를 재현 할 수 있기를 희망하는 많은 작업이 필요합니다. 예외를 올바르게 처리하면 충돌을 예방할뿐만 아니라 유용한 정보를 제공 할 수 있습니다. "프로그램에서 오류가 발생했습니다. Z가 너무 커서 XYZ가 메소드 M에 유효하지 않습니다."라는 메시지가 표시됩니다. 또는 의미가 무엇인지 모르더라도 볼 곳을 정확히 알고 있어야합니다. 또한 귀사 / 회사의 비즈니스 관행에 따라 이러한 문제를 해결하지 못할 수도 있으므로 문제를 좋은지도로 남겨 두는 것이 가장 좋습니다.

내 대답의 짧은 버전은 첫 번째 옵션이 가장 좋습니다.

Inconsistent input -> no action + notify caller

1

나는 프로그래밍에서 대학 수업을 진행하는 동안이 같은 문제로 어려움을 겪었습니다. 나는 편집증 쪽을 향해 몸을 기울이고 모든 것을 확인하는 경향이 있지만 이것은 잘못된 행동이라고 들었습니다.

「계약에 의한 디자인」을 배웠습니다. 전제 조건, 불변량 및 사후 조건은 주석 및 설계 문서에 명시되어 있어야합니다. 코드의 일부를 구현하는 사람은 소프트웨어 설계자를 신뢰하고 전제 조건 (내 메소드가 처리 할 수있는 입력 및 전송되지 않는 입력)을 포함하는 사양을 준수하여 소프트웨어 설계자에게 권한을 부여해야합니다. . 모든 메소드 호출에서 과도한 점검으로 인해 부풀어 오릅니다.

프로그램 반복 (전제 조건, 불변량, 사후 조건의 유효성 검증)을 검증하기 위해 빌드 반복 중에 어설 션을 사용해야합니다. 그런 다음 프로덕션 컴파일에서 어설 션이 해제됩니다.


0

"어설 션"을 사용하는 것은 물론 "개인"방법으로 만 동료 개발자에게 잘못하고 있음을 알리는 방법입니다 . 그것들을 활성화 / 비활성화하면 컴파일 타임에 추가 / 제거 할 수있는 플래그 일뿐이므로 프로덕션 코드에서 어설 션을 쉽게 제거 할 수 있습니다. 알고있는 좋은 도구도있다 당신이 어떻게 든 자신의 방법에 잘못을하고있는가.

공개 / 보호 된 메소드 내에서 입력 매개 변수를 확인하려면 방어 적으로 작업하고 매개 변수를 확인하고 InvalidArgumentException 등을 던지는 것을 선호합니다. 그것이 여기에있는 이유입니다. 또한 API 작성 여부에 따라 다릅니다. API 인 경우, 폐쇄 소스 인 경우 더 많은 경우 개발자가 무엇이 잘못되었는지 정확하게 알 수 있도록 모든 것을 검증하십시오. 그렇지 않으면 다른 개발자가 소스를 사용할 수 있으면 흑백이 아닙니다. 선택 사항과 일관성을 유지하십시오.

편집 : 예를 들어 Oracle JDK를 보면 "null"을 확인하지 않고 코드가 충돌하지 않는다는 것을 알 수 있습니다. 어쨌든 NullPointerException이 발생하기 때문에 null을 확인하고 명시 적 예외를 발생시키는 이유가 무엇입니까? 나는 그것이 의미가 있다고 생각합니다.


Java에서는 널 포인터 예외가 발생합니다. C ++에서 null 포인터는 응용 프로그램을 중단시킵니다. 다른 예가있을 수 있습니다. 0으로 나누기, 범위를 벗어난 인덱스 등.
Giorgio
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.