본문 바로가기

화면 설계와 피드백

사용자가 판단에 필요한 정보를 고르고, 입력 중, 응답 대기, 성공, 실패의 화면 상태를 모임의 상태와 나누어 설계합니다. 결과 코드를 안내 문구와 다음 행동으로 바꾸는 표현 모델을 실행해 보고, 접근성과 연결이 끊긴 경우의 안내까지 검토합니다.

신청 버튼을 눌렀는데 화면에 아무 변화가 없으면 사용자는 한 번 더 누릅니다. 저장은 되었는데 완료 안내가 없으면 실패했다고 생각하고, 실패한 뒤 입력이 지워지면 같은 내용을 다시 적어야 합니다. 화면 설계는 보기 좋게 배치하는 일만이 아니라, 사용자가 지금 무슨 상태이고 다음에 무엇을 하면 되는지 알게 하는 일입니다. 이 글에서는 앞에서 정한 결과 코드를 사용자가 읽을 안내로 바꾸고, 입력 중, 응답 대기, 성공, 실패의 화면 상태를 설계합니다. 예제는 명령줄에서 안내를 만드는 부분만 확인하며, 실제 브라우저 화면은 만들지 않습니다.

사용자가 판단할 정보를 고른다

사람이 시스템에 무언가를 입력하고 응답을 받는 접점을 사용자 인터페이스(user interface, UI)라고 합니다. 버튼과 입력란이 있는 화면뿐 아니라 명령줄도 UI입니다. 사용성(usability)은 정해진 사용자가 정해진 상황에서 목표를 얼마나 정확하고 수월하게 이루는지를 가리키므로, 누가 무엇을 하려는지 정하지 않고 색이나 배치만 평가해서는 사용성을 판단할 수 없습니다.

독서 모임 신청 화면에는 사용자가 신청할지 판단하는 데 필요한 정보를 둡니다. 남은 자리는 참가자 수로 계산해 보여 주지만, 그 계산에 쓰는 참가자 아이디 목록까지 보여 줄 필요는 없습니다. 판단에 필요한 정보와 내부 데이터를 나누는 것이며, 화면에 남은 자리가 있다고 나왔더라도 실제 요청을 받을 때는 시스템이 규칙을 다시 검사합니다.

화면 영역사용자의 질문보여 줄 정보
모임 요약어디에 신청하는가제목, 일시, 정원
모집 정보지금 신청할 수 있는가모집 중인지 마감인지, 남은 자리
입력무엇을 적어야 하는가회원 아이디의 이름표와 형식 설명
처리 결과신청되었는가성공 또는 거절의 이유, 다음 행동
내 참가 내역나중에도 확인할 수 있는가신청한 모임과 취소하는 방법

입력 형식은 입력란 안에 흐리게 표시되는 예시 문구(placeholder)만으로 알려 주지 않습니다. 이 문구는 입력을 시작하면 사라져서, 막상 형식을 확인하고 싶을 때는 볼 수 없기 때문입니다. 이름표와 짧은 설명은 입력란 옆에 계속 보이게 둡니다. 다만 이 실습의 아이디 입력은 로그인을 구현한 화면이 아니라, 이미 받은 아이디로 신청 규칙을 시험하는 장치라는 점을 기억해 둡니다.

화면 상태와 모임 상태를 나눈다

화면은 입력 중(EDITING), 응답 대기(SENDING), 성공(SUCCEEDED), 실패(FAILED) 같은 자기만의 상태를 가집니다. 이 값은 상태와 데이터 모델에서 정한 모임의 OPEN, CLOSED와 다른 대상의 상태입니다. SENDING은 요청을 보내고 응답을 기다린다는 뜻일 뿐, 모임의 모집이 마감되었다는 뜻이 아닙니다.

요청 상태에 따라 안내와 다음 행동을 바꾼다

입력을 유지하며 신청을 준비한다. 거절 또는 장애를 확인한 뒤 가능한 다음 행동을 알린다. 각 단계의 값과 전환 이유를 비교합니다.

SOFTWARE ENGINEERING · CH3-3

요청 상태에 따라 안내와 다음 행동을 바꾼다

전송만 끝난 상태와 저장이 완료된 상태를 구별합니다.

요청 상태에 따라 안내와 다음 행동을 바꾼다입력 중에서 요청 대기로 이동하고 저장 성공은 완료 안내, 거절 또는 장애는 입력을 보존하는 복구 안내로 분기합니다.신청 요청저장 성공거절·장애입력 중EDITING응답을 기다림SENDING완료 안내SUCCEEDED복구 안내FAILED실패한 입력은 보존한다

입력 중에서 요청 대기로 이동하고 저장 성공은 완료 안내, 거절 또는 장애는 입력을 보존하는 복구 안내로 분기합니다.

이 예의 조건

  • 본문과 전체 도표의 예를 따라 입력 중부터 실패 안내까지 진행합니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
입력 중

입력을 유지하며 신청을 준비한다.

  • 요청·화면 1: EDITING
  • 요청·화면 2: 입력 보유
    응답 대기

    전송 완료는 저장 성공이 아니다.

    • 요청·화면 1: SENDING
    • 요청·화면 2: 중복 클릭 억제
    • 저장 결과 대기
    실패 안내

    거절 또는 장애를 확인한 뒤 가능한 다음 행동을 알린다.

    • 요청·화면 1: FAILED
    • 요청·화면 2: 입력 보존
    • 요청·화면 3: FULL이면 다른 모임 확인
    • 저장 성공이면 SUCCEEDED라는 별도 분기 유지

    요청 상태에 따라 안내와 다음 행동을 바꾼다

    입력을 유지하며 신청을 준비한다. 거절 또는 장애를 확인한 뒤 가능한 다음 행동을 알린다. 각 단계의 값과 전환 이유를 비교합니다.

    SOFTWARE ENGINEERING · CH3-3

    요청 상태에 따라 안내와 다음 행동을 바꾼다

    전송만 끝난 상태와 저장이 완료된 상태를 구별합니다.

    요청 상태에 따라 안내와 다음 행동을 바꾼다입력 중에서 요청 대기로 이동하고 저장 성공은 완료 안내, 거절 또는 장애는 입력을 보존하는 복구 안내로 분기합니다.신청 요청저장 성공거절·장애입력 중EDITING응답을 기다림SENDING완료 안내SUCCEEDED복구 안내FAILED실패한 입력은 보존한다

    입력 중에서 요청 대기로 이동하고 저장 성공은 완료 안내, 거절 또는 장애는 입력을 보존하는 복구 안내로 분기합니다.

    이 예의 조건

    • 본문과 전체 도표의 예를 따라 입력 중부터 실패 안내까지 진행합니다.
    예의 값과 변화
    관찰·상황값·보충 정보변화·이유
    입력 중

    입력을 유지하며 신청을 준비한다.

    • 요청·화면 1: EDITING
    • 요청·화면 2: 입력 보유
      응답 대기

      전송 완료는 저장 성공이 아니다.

      • 요청·화면 1: SENDING
      • 요청·화면 2: 중복 클릭 억제
      • 저장 결과 대기
      실패 안내

      거절 또는 장애를 확인한 뒤 가능한 다음 행동을 알린다.

      • 요청·화면 1: FAILED
      • 요청·화면 2: 입력 보존
      • 요청·화면 3: FULL이면 다른 모임 확인
      • 저장 성공이면 SUCCEEDED라는 별도 분기 유지

      응답을 기다리는 동안에는 신청 버튼을 잠시 누를 수 없게 하고 "신청을 처리하고 있습니다"라고 알릴 수 있습니다. 이렇게 하면 같은 화면에서 실수로 여러 번 누르는 일은 줄지만, 다른 창이나 다시 보낸 요청까지 막지는 못하므로 중복 검사는 여전히 시스템이 해야 합니다. 성공 안내는 저장이 끝났다는 응답을 받은 뒤에 보여 줍니다. 요청을 보내기만 하고 "신청 완료"를 표시하면, 저장에 실패한 경우에도 사용자는 신청되었다고 믿게 됩니다.

      실패했을 때는 원인의 종류를 나누어 안내합니다. 형식이 틀린 아이디는 사용자가 입력을 고쳐 해결할 수 있는 입력 오류입니다. 반면 정원이 찬 것이나 모집이 마감된 것은 입력이 잘못된 것이 아니라 모임의 사정이므로, "잘못된 회원"이라고 표시하면 안 됩니다. 원인과 할 수 있는 다음 행동을 짝지어 알려 주어야 소용없는 재시도가 줄어듭니다.

      결과 코드와 안내 문구를 나눈다

      FULL 같은 결과 코드는 프로그램이 분기할 때 쓰는 고정된 값이고, 안내 문구는 사람이 읽을 표현입니다. 신청 규칙을 판단하는 함수 안에 한국어 문장을 직접 넣으면, 문구 하나를 고치는 일이 규칙을 고치는 일과 섞입니다. 그래서 결과를 화면에서 쓸 값으로 바꾸어 담은 표현 모델(view model)을 따로 만듭니다.

      다음 코드를 reading-lab/examples/ch3-3.mjs로 저장하고 node examples/ch3-3.mjs로 실행합니다. presentResult는 신청 규칙을 다시 판단하지 않고, register가 돌려준 결과 코드만 안내로 바꿉니다.

      import { createSession, register } from "../core.mjs";
      
      const messages = {
        INVALID_MEMBER: "회원 아이디는 영문 소문자로 시작하고 소문자, 숫자, 하이픈으로 24자까지 쓸 수 있습니다.",
        CLOSED: "모집이 마감되었습니다. 다른 모임을 확인해 주세요.",
        ALREADY_REGISTERED: "이미 신청한 모임입니다. 참가 내역을 확인해 주세요.",
        FULL: "남은 자리가 없습니다. 다른 모임을 확인해 주세요.",
        REGISTERED: "신청이 완료되었습니다. 참가 내역에서 확인할 수 있습니다.",
      };
      
      function presentResult(result) {
        if (!Object.hasOwn(messages, result.code)) {
          throw new Error("Unknown result code: " + result.code);
        }
        return {
          text: messages[result.code],
          tone: result.ok ? "success" : "notice",
          keepInput: !result.ok,
        };
      }
      
      const session = createSession({ id: "book-04", title: "짧은 소설 읽기", capacity: 1 });
      const first = register(session, "min");
      const second = register(first.state, "hana");
      for (const result of [first, second]) {
        const view = presentResult(result);
        console.log(view.tone, view.keepInput, view.text);
      }
      try {
        presentResult({ ok: false, code: "NOT_REGISTERED" });
      } catch (error) {
        console.log(error.message);
      }
      success false 신청이 완료되었습니다. 참가 내역에서 확인할 수 있습니다.
      notice true 남은 자리가 없습니다. 다른 모임을 확인해 주세요.
      Unknown result code: NOT_REGISTERED

      정원이 1인 모임에서 min의 신청은 성공 안내를, hana의 신청은 남은 자리가 없다는 안내를 받았습니다. 정원이 찬 경우에는 같은 입력으로 다시 누르라고 하지 않고 다른 모임을 확인하라고 안내합니다. tone은 색을 직접 정하는 값이 아니라 성공인지 알림인지 하는 표현의 의도를 나타내며, 실제 색은 화면을 만드는 쪽에서 정합니다. result.ok ? "success" : "notice"는 조건 연산자(conditional operator)로, ? 앞의 result.ok가 참이면 : 앞의 값을, 거짓이면 : 뒤의 값을 고릅니다.

      messages에 없는 코드가 들어오면 성공처럼 넘기지 않고 오류를 던집니다. 취소의 결과 코드인 NOT_REGISTERED가 신청 안내로 잘못 들어온 것처럼, 코드와 화면이 서로 다른 결과 목록을 알고 있다는 사실을 바로 드러내기 위해서입니다. Object.hasOwn(messages, code)는 messages 객체에 직접 적은 속성인지만 확인하는데, 이 차이는 연습 문제 3에서 다시 봅니다.

      keepInput은 실패하면 입력을 남겨 둔다는 이 화면의 규칙입니다. !result.ok의 !는 참과 거짓을 뒤집으므로, keepInput은 신청에 실패했을 때만 true가 됩니다. 아이디가 잘못되었을 때 사용자가 틀린 부분만 고치면 되도록 하기 위해서입니다. 성공한 뒤 입력을 비울지는 화면의 목표에 따라 달라질 수 있는데, 여기서는 성공하면 참가 내역 화면으로 넘어간다고 가정했습니다.

      접근성과 복구를 함께 검토한다

      빨간 테두리만으로 실패를 알리면, 색을 구별하기 어려운 사용자는 무엇이 잘못되었는지 알 수 없습니다. 오류는 글로 적고 어느 입력란의 오류인지 연결해 두어야 하며, 키보드만으로 입력, 신청, 취소를 할 수 있고 지금 어디에 초점이 있는지 보여야 합니다. 실제 브라우저 화면에서는 입력란의 이름표, 설명과 오류 문구의 연결, 응답 뒤에 바뀐 내용을 화면 낭독기에 알리는 방법까지 갖추어야 하며, HTML로 이름표와 키보드 접근성을 갖추는 기본은 시맨틱 HTML과 접근성에서 다룹니다. W3C WAI의 폼 알림 안내가 오류의 원인과 해결 방법, 성공 확인, 입력 가까이에 두는 안내를 자세히 다룹니다. 안내 문구를 만드는 함수를 시험했다고 해서 화면의 접근성까지 확인한 것은 아닙니다.

      연결이 끊기는 경우도 생각해야 합니다. 서버가 저장을 마친 뒤 응답만 끊겼다면 사용자는 신청이 되었는지 알 수 없습니다. 이때 "실패했으니 다시 신청하세요"라고 단정하기보다, 참가 내역에서 신청 여부를 확인하는 방법을 알려 주는 편이 안전합니다. 지금의 예제는 네트워크를 쓰지 않으므로 이 상황은 요구와 설계로 적어 두고, 동시 저장과 복구에서 저장 방식과 함께 다시 다룹니다.

      더 깊이: WCAG 2.2가 새로 더한 기준

      웹 접근성의 기준으로 가장 널리 쓰는 것은 W3C의 WCAG(Web Content Accessibility Guidelines)입니다. 2023년 10월 5일 W3C 권고안이 된 WCAG 2.2는 2.1에 성공 기준(success criterion) 아홉 개를 더했고, W3C의 정리에 따르면 그 가운데 몇 개는 이 글의 신청 화면과 바로 이어집니다. 3.3.7 중복 입력(Redundant Entry, A등급)은 같은 과정에서 사용자가 이미 입력했거나 받은 정보를 다시 요구할 때 자동으로 채우거나 고를 수 있게 하라는 기준으로, 실패해도 입력을 남겨 두는 keepInput 규칙과 같은 방향입니다.

      2.5.8 대상 크기(Target Size (Minimum), AA등급)는 누르는 대상을 예외가 아니라면 적어도 24 × 24 CSS 픽셀로 두게 하고, 2.4.11 초점이 가려지지 않음(Focus Not Obscured (Minimum), AA등급)은 키보드 초점을 받은 요소가 고정된 머리글이나 알림에 완전히 가려지지 않게 합니다. 3.3.8 접근 가능한 인증(Accessible Authentication (Minimum), AA등급)은 로그인할 때 비밀번호 기억이나 퍼즐 풀이 같은 인지 기능 시험을 대안이나 돕는 수단 없이 요구하지 않게 하는 기준이어서, 이 교재가 범위 밖에 둔 로그인 화면을 만들 때 따져야 합니다. 한편 예전의 4.1.1 구문 분석(Parsing)은 더 이상 쓸모가 없다고 보아 2.2에서 빠졌습니다.

      작은 사용성 시험

      화면 초안은 종이에 그려도 됩니다. 시험에 참여한 사람에게 "열린 모임에 신청하고, 참가 내역에서 확인해 보세요" 같은 과제를 주고, 따로 안내하지 않은 채 어디에서 멈추는지 지켜봅니다. 과제를 끝냈는지, 잘못 누른 곳이 어디인지, 무슨 말을 이해하지 못했는지를 기록합니다. "디자인이 마음에 드시나요?"라는 질문만으로는 사용자가 목표를 이룰 수 있는지 알 수 없습니다.

      몇 명만 시험하면 충분하다는 정해진 규칙은 없습니다. 새로 발견되는 문제가 계속 나오거나 아직 만나 보지 못한 사용자 집단이 있다면 시험의 범위를 넓힙니다. 시험의 목적과 사용자의 특성에 맞게 참여자를 고르고, 결과를 알릴 때는 시험한 범위를 함께 밝힙니다.

      정리

      • 사용성은 정해진 사용자가 정해진 상황에서 목표를 얼마나 정확하고 수월하게 이루는지를 말하며, 화면에는 사용자가 판단에 필요한 정보를 둡니다.
      • 입력 중, 응답 대기, 성공, 실패 같은 화면 상태는 모임의 모집 상태와 다른 대상의 상태입니다.
      • 성공 안내는 저장이 끝났다는 응답을 받은 뒤에 보여 주고, 실패는 입력 오류와 모임의 사정을 나누어 다음 행동과 함께 안내합니다.
      • 결과 코드는 프로그램이 분기하는 값이고 안내 문구는 표현 모델이 정하므로, 문구를 고쳐도 신청 규칙은 바뀌지 않습니다.
      • 오류는 색만이 아니라 글로 알리고, 연결이 끊겨 결과를 모르는 경우에는 확인하는 방법을 안내합니다.

      연습 문제

      1. 도서 추천 폼의 결과

      도서 추천 폼은 제목과 추천 이유를 받습니다. 빈 제목은 입력 오류, 추천할 수 없는 책은 정책에 따른 거절, 디스크에 쓰지 못한 것은 저장 장애이며, 성공하면 추천 목록으로 이동합니다. 네 가지 결과마다 안내 문구, 입력을 남길지, 다음 행동을 표로 정리하세요.

      해설 보기

      빈 제목에는 제목 입력란을 가리키며 제목을 적어 달라고 안내하고, 추천할 수 없는 책에는 그 기준과 함께 다른 책을 고르도록 안내합니다. 저장 장애에는 저장을 마치지 못했다는 사실과 입력한 내용이 그대로 있다는 것, 다시 시도하는 방법을 알립니다. 성공하면 완료 사실을 알리고 추천 목록으로 이동합니다. 실패한 세 경우는 모두 추천 이유를 남겨 두고, 성공한 경우만 다음 화면으로 넘어갑니다. 세 실패에 모두 "입력 오류"라는 제목을 붙이면, 사용자는 고칠 것이 없는 저장 장애에서도 입력을 고치려 하게 됩니다.

      2. 완료 안내가 먼저 나오는 코드

      다음 함수는 저장 장애가 나도 "추천이 완료되었습니다."를 보여 줍니다. save는 { ok: true }나 { ok: false }를 돌려준다고 할 때, 무엇이 잘못되었는지 설명하고 고쳐 보세요.

      function submit(form, save) {
        save(form);
        return { text: "추천이 완료되었습니다.", keepInput: false };
      }
      해설 보기

      save가 돌려준 결과를 보지 않고 완료 안내를 만들기 때문에, 저장에 실패해도 사용자는 완료되었다고 믿고 입력도 사라집니다. 완료 안내는 저장이 성공했다는 결과를 확인한 분기에서만 만들어야 합니다.

      function submit(form, save) {
        const result = save(form);
        if (!result.ok) {
          return { text: "저장하지 못했습니다. 입력한 내용은 그대로 있으니 다시 시도해 주세요.", keepInput: true };
        }
        return { text: "추천이 완료되었습니다.", keepInput: false };
      }

      여기서 더 나아가면, 저장 함수는 결과 코드만 돌려주고 문구는 본문의 presentResult 같은 표현 함수가 정하도록 나눌 수 있습니다. 그러면 문구를 바꾸어도 저장이나 추천 규칙에는 영향이 없습니다.

      3. in과 Object.hasOwn

      본문의 presentResult에서 Object.hasOwn(messages, result.code) 대신 result.code in messages로 검사하면, 코드에 "toString"이 들어왔을 때 어떻게 되나요?

      해설 보기

      in 연산자는 객체에 직접 적은 속성뿐 아니라 모든 객체가 물려받는 toString 같은 속성도 찾으므로, "toString" in messages는 true가 됩니다. 그래서 알 수 없는 코드인데도 검사를 통과하고, messages["toString"]이 문자열이 아니라 함수이므로 text에 안내 문구 대신 함수가 들어갑니다. 이 값을 출력하면 [Function: toString]처럼 사용자에게 의미 없는 내용이 나옵니다. Object.hasOwn은 messages에 직접 적은 다섯 코드만 인정하므로 이런 값을 오류로 거절합니다.

      유스케이스는 사용자의 목표를, 상태 모델은 무엇이 허용되는지를, 화면 설계는 그 판단을 사용자가 알아볼 수 있는 표현을 정합니다. 다음 장의 책임을 나누는 모듈에서는 이 세 가지가 한 함수에 뒤엉키지 않도록 모듈을 나누고, 변경이 어디까지 영향을 주는지 살펴봅니다.


      원자료와 더 읽기

      장원사이버평생교육원 「소프트웨어공학」 PDF 물리 186–200쪽의 주제를 바탕으로 설명, 도표, 실습을 새로 구성했습니다. 원자료의 화면 설계 지침 가운데 숫자로 정한 기준은 모든 화면의 고정 규칙으로 쓰지 않고, 작업의 목표에 맞게 검토했습니다. 폼의 안내는 W3C WAI 폼 알림 안내를 참고합니다.