정보 구조(IA) 설계와 카드 소팅: 사용자 멘탈 모델에 맞는 네비게이션

UX 디자인

정보 구조카드 소팅IA 설계사용자 리서치사이트맵

이 글은 누구를 위한 것인가

  • 서비스 메뉴 구조가 "우리 논리"가 아닌 "사용자 논리"로 설계됐는지 의심하는 팀
  • 카드 소팅을 처음 해보고 싶은 UX 리서처
  • 사이트 리뉴얼 시 정보 구조를 재설계해야 하는 팀

들어가며

"이 메뉴가 어디 있는지 도저히 못 찾겠어요" — 이것은 정보 구조(IA) 실패다. 우리 팀의 논리로 메뉴를 만들면 사용자가 찾을 수 없다. 카드 소팅은 사용자가 정보를 어떻게 분류하는지 직접 데이터로 확인하는 방법이다.

이 글은 bluefoxdev.kr의 UX 리서치 방법론 를 참고하여 작성했습니다.


1. 정보 구조 설계 프로세스

[IA 설계 단계]

1단계 - 콘텐츠 인벤토리:
  현재 서비스의 모든 페이지·기능 목록화
  스프레드시트에 URL + 제목 + 카테고리 정리

2단계 - 카드 소팅 (사용자 리서치):
  오픈 카드 소팅: 사용자가 자유롭게 그룹화
  클로즈드 카드 소팅: 미리 정의된 카테고리로 분류
  하이브리드: 기존 카테고리 + 새 카테고리 추가 가능
  
  참여자: 최소 15-20명 (유사도 매트릭스 의미 있음)
  도구: Maze, Optimal Workshop, Miro

3단계 - 유사도 매트릭스 분석:
  같은 그룹에 넣은 빈도 → 함께 배치
  다른 그룹에 넣은 항목 → 분리 또는 중복 배치

4단계 - 사이트맵 초안:
  리서치 결과 기반 계층 구조 설계

5단계 - 트리 테스트:
  "이 작업을 수행하려면 어디로 가시겠습니까?"
  실제 찾기 성공률 측정

6단계 - 반복:
  성공률 낮은 항목 재배치

2. 카드 소팅 분석

import pandas as pd
import numpy as np
from scipy.cluster.hierarchy import linkage, fcluster
import matplotlib.pyplot as plt

def analyze_card_sorting(sorting_data: list[dict]) -> dict:
    """
    카드 소팅 데이터 분석
    sorting_data: [{"participant": "P1", "card": "주문 내역", "group": "마이페이지"}]
    """
    
    df = pd.DataFrame(sorting_data)
    
    # 카드별 공동 분류 빈도 매트릭스
    cards = df["card"].unique()
    n = len(cards)
    card_to_idx = {card: i for i, card in enumerate(cards)}
    
    co_occurrence = np.zeros((n, n))
    
    # 같은 참여자가 같은 그룹에 넣은 카드 쌍 카운트
    for participant, group_df in df.groupby("participant"):
        for group, cards_in_group in group_df.groupby("group")["card"]:
            cards_list = cards_in_group.tolist()
            for i in range(len(cards_list)):
                for j in range(i + 1, len(cards_list)):
                    idx_i = card_to_idx[cards_list[i]]
                    idx_j = card_to_idx[cards_list[j]]
                    co_occurrence[idx_i][idx_j] += 1
                    co_occurrence[idx_j][idx_i] += 1
    
    # 총 참여자 수로 정규화 (0-100%)
    n_participants = df["participant"].nunique()
    similarity_matrix = (co_occurrence / n_participants * 100).round(1)
    
    # 군집 분석
    distance_matrix = 100 - similarity_matrix
    Z = linkage(distance_matrix, method="ward")
    clusters = fcluster(Z, t=3, criterion="maxclust")  # 3개 그룹으로
    
    # 결과 정리
    card_clusters = {}
    for i, card in enumerate(cards):
        cluster_id = clusters[i]
        if cluster_id not in card_clusters:
            card_clusters[cluster_id] = []
        card_clusters[cluster_id].append(card)
    
    return {
        "similarity_matrix": similarity_matrix,
        "suggested_groups": card_clusters,
        "n_participants": n_participants,
    }

def find_problematic_labels(tree_test_results: list[dict]) -> list[dict]:
    """트리 테스트에서 실패율 높은 경로 찾기"""
    
    df = pd.DataFrame(tree_test_results)
    
    # 각 태스크별 성공률
    task_success = df.groupby("task_id").agg(
        success_rate=("success", "mean"),
        avg_time=("time_seconds", "mean"),
        first_click_correct=("first_click_correct", "mean"),
    ).reset_index()
    
    # 성공률 50% 미만인 태스크
    problematic = task_success[task_success["success_rate"] < 0.5]
    
    return problematic.to_dict("records")

마무리

카드 소팅에서 가장 가치 있는 인사이트는 "이것과 저것이 같은 그룹에 있어야 하는지"다. 주문 내역이 마이페이지 하위인지, 주문 하위인지 — 사용자 15명의 데이터가 어떤 주장보다 강력하다. 트리 테스트로 IA 초안을 검증하면 출시 후 "찾기 어렵다" 피드백을 크게 줄일 수 있다.