사용자 흐름

User flow

한 과제를 시작부터 끝까지 완료하기까지 사용자가 거치는 화면과 결정 지점을 순서도로 그린 다이어그램.

다른 이름: Flow diagram유저 플로우
···
html
<div class="stage">
  <svg viewBox="0 0 420 120" class="flow">
    <g id="edges"></g>
    <g id="nodes"></g>
    <circle id="ball" r="5" class="ball" />
  </svg>
</div>
css
.stage{width:96%;height:88%}
.flow{width:100%;height:100%}
.box{fill:var(--surface);stroke:var(--line);stroke-width:1.5}
.box.diamond{fill:var(--surface)}
.edge{stroke:var(--muted);stroke-width:1.4;fill:none;marker-end:url(#arrow)}
text{font:700 8.5px sans-serif;fill:var(--fg);text-anchor:middle}
.ball{fill:var(--accent)}
js
const NS = 'http://www.w3.org/2000/svg';
const svg = document.querySelector('.flow');
const defs = document.createElementNS(NS, 'defs');
defs.innerHTML = '<marker id="arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0 0 L8 4 L0 8 Z" fill="var(--muted)"/></marker>';
svg.prepend(defs);
const edgesG = document.getElementById('edges');
const nodesG = document.getElementById('nodes');
const nodes = [
  { x: 30, y: 60, w: 46, h: 30, t: 'Start' },
  { x: 130, y: 60, w: 58, h: 30, t: 'Login' },
  { x: 250, y: 30, w: 50, h: 28, t: 'Error', diamond: true },
  { x: 250, y: 90, w: 58, h: 30, t: 'Home' },
  { x: 380, y: 90, w: 30, h: 30, t: 'Done', circle: true },
];
const edges = [[0, 1], [1, 2], [1, 3], [3, 4]];
function drawNode(n) {
  if (n.circle) {
    const c = document.createElementNS(NS, 'circle');
    c.setAttribute('cx', n.x); c.setAttribute('cy', n.y); c.setAttribute('r', 15);
    c.setAttribute('class', 'box');
    nodesG.appendChild(c);
  } else {
    const r = document.createElementNS(NS, 'rect');
    r.setAttribute('x', n.x - n.w / 2); r.setAttribute('y', n.y - n.h / 2);
    r.setAttribute('width', n.w); r.setAttribute('height', n.h);
    r.setAttribute('rx', n.diamond ? 14 : 5);
    r.setAttribute('class', 'box');
    nodesG.appendChild(r);
  }
  const t = document.createElementNS(NS, 'text');
  t.setAttribute('x', n.x); t.setAttribute('y', n.y + 3);
  t.textContent = n.t;
  nodesG.appendChild(t);
}
nodes.forEach(drawNode);
edges.forEach(([a, b]) => {
  const p = document.createElementNS(NS, 'path');
  const A = nodes[a], B = nodes[b];
  p.setAttribute('d', 'M' + (A.x + (A.w || 30) / 2) + ' ' + A.y + ' L' + (B.x - (B.w || 30) / 2) + ' ' + B.y);
  p.setAttribute('class', 'edge');
  edgesG.appendChild(p);
});
const ball = document.getElementById('ball');
const path = [0, 1, 3, 4];
let i = 0;
function step() {
  const n = nodes[path[i % path.length]];
  const above = n.circle ? 22 : (n.h || 30) / 2 + 12;
  ball.setAttribute('cx', n.x);
  ball.setAttribute('cy', n.y - above);
  i++;
}
step();
setInterval(step, 900);

사용자 흐름은 "회원가입"처럼 하나의 구체적인 과제를 골라, 시작 지점부터 끝 지점까지 어떤 화면을 거치고 어디서 갈림길(성공/실패, 로그인 여부)이 생기는지를 상자와 화살표, 조건 분기(마름모)로 그립니다. 화면의 세부 디자인이 아니라 화면 사이의 이동 경로 자체가 다이어그램의 내용입니다.

정보 구조가 사이트 전체 콘텐츠를 계층으로 나눈 "지도"라면, 사용자 흐름은 그 지도 위에서 한 사람이 실제로 걷는 "경로" 하나입니다. 같은 정보 구조 위에서도 과제(로그인 사용자의 결제 vs 비회원의 결제)에 따라 서로 다른 사용자 흐름이 여러 개 나올 수 있습니다.

개발 착수 전에 그려두면 "이 화면 다음에 뭐가 와야 하는지"에 대한 팀의 암묵적 가정이 서로 다르다는 게 일찍 드러납니다. 에러 상태나 예외 분기(비밀번호 틀림, 재고 없음)를 빼먹기 쉬운데, 그 갈림길이야말로 실제 구현에서 가장 자주 빠지는 부분입니다.

언제 쓰나

개발 착수 전, 화면과 화면 사이의 이동 경로와 예외 분기를 팀이 같은 그림으로 합의해야 할 때 씁니다.