Link | CoUI

Link

내부 또는 외부 URL로 이동하는 링크 텍스트 컴포넌트

Link#

내부 라우팅 또는 외부 URL로 이동하는 링크 텍스트 컴포넌트입니다. 다양한 시각적 변형과 외부 링크 아이콘을 지원합니다.

Live Preview#

Flutter
Link(
  href: '#',
  child: Text('Click here'),
)
Link(
  href: '#',
  child: Text('Click here'),
)

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 본문 텍스트 중간에 인라인 링크가 필요할 때
  • 외부 사이트로 이동하는 링크를 명시적으로 표시할 때 (새 창 아이콘)
  • 이용약관, 개인정보처리방침 같은 보조 링크가 필요할 때

대신 다른 컴포넌트를 사용하세요:

  • Button: 사용자 액션을 유발하는 클릭 가능한 요소에 (탐색이 아닌 경우)
  • Breadcrumb: 계층적 페이지 탐색 경로를 표시할 때

기본 사용법 (Basic Usage)#

// 기본 링크 (내부 이동)
Link(
  onTap: handleNavigateToProfile,
  child: Text('프로필 보기'),
)

// 외부 URL 링크
Link(
  href: 'https://coui.cocode.im',
  isExternal: true,
  child: Text('CoUI 문서'),
)

// Underline 변형
Link(
  onTap: handleNavigateToSettings,
  variant: LinkVariant.underline,
  child: Text('설정'),
)

// Muted 변형
Link(
  href: 'https://example.com/terms',
  isExternal: false,
  variant: LinkVariant.muted,
  child: Text('이용약관'),
)
// 기본 내부 링크
Link(
  href: '/profile',
  child: text('프로필 보기'),
)

// 외부 링크 (새 탭, external 파라미터 사용)
Link(
  href: 'https://coui.cocode.im',
  external: true,
  child: text('CoUI 문서'),
)

// Underline 항상 표시 (underline: true가 기본값)
Link(
  href: '/settings',
  underline: true,
  child: text('설정'),
)

// Underline 숨김 (Muted 스타일)
Link(
  href: '/terms',
  underline: false,
  child: text('이용약관'),
)

Props / Parameters#

속성타입기본값설명
childWidget필수링크로 표시할 위젯
href String? null 이동할 URL (외부/내부)
onTap VoidCallback? null 탭 핸들러 (href 대신 사용)
variant LinkVariant default 스타일 변형
isExternal bool false 외부 링크 여부. true이면 새 창으로 열기 및 아이콘 표시

변형 (Variants)#

Default#

기본 링크 색상을 사용합니다. 호버 시 언더라인이 나타납니다.

Link(
  onTap: handleNavigate,
  variant: LinkVariant.defaultVariant,
  child: Text('기본 링크'),
)

Underline#

항상 언더라인이 표시됩니다. 본문 내 링크에 적합합니다.

Link(
  onTap: handleNavigate,
  variant: LinkVariant.underline,
  child: Text('밑줄 링크'),
)

Muted#

더 낮은 대비의 색상으로 표시됩니다. 부가 정보 링크에 적합합니다.

Link(
  href: 'https://example.com/privacy',
  isExternal: false,
  variant: LinkVariant.muted,
  child: Text('개인정보처리방침'),
)

외부 링크 (isExternal)#

외부 링크 아이콘을 자동으로 표시하고 새 창에서 열립니다.

Link(
  href: 'https://flutter.dev',
  isExternal: true,
  child: Text('Flutter 공식 사이트'),
)

동작 스펙 (Behavior)#

인터랙션#

  • 클릭/탭: onTap 호출 또는 href URL로 이동
  • 호버: default 변형에서 언더라인 표시; 커서 포인터로 변경
  • 외부 링크: isExternal: true 시 새 탭/창에서 열림, 우측에 외부 링크 아이콘 자동 추가

상태 전환#

  • defaulthover: 언더라인 표시 (200ms)
  • defaultvisited: 방문 후 색상 변경 (Web 전용)

애니메이션#

  • 호버 언더라인: 200ms ease-in-out

사용 가이드라인 (Usage Guidelines)#

✅ Do#

본문 내 링크에는 underline variant로 가시성 확보

Text.rich(
  TextSpan(
    text: '자세한 내용은 ',
    children: [
      WidgetSpan(
        child: CouiLink(
          onTap: handleNavigateToHelp,
          variant: LinkVariant.underline,
          child: const Text('도움말'),
        ),
      ),
      const TextSpan(text: '를 참조하세요.'),
    ],
  ),
)

본문 텍스트 중 링크를 언더라인으로 구분해야 사용자가 클릭 가능한 텍스트를 인식한다.


❌ Don't#

링크처럼 보이는 일반 텍스트를 클릭 불가로 표시하지 않기

// ❌ 파란색인데 클릭 안 됨
Text('프로필 보기', style: TextStyle(color: Colors.blue))

링크처럼 보이는 텍스트가 클릭되지 않으면 사용자 신뢰를 잃는다. 파란색 텍스트는 반드시 클릭 가능해야 한다.

✅ Do#

외부 링크에는 isExternal: true로 명시적 안내

CouiLink(
  href: 'https://external-site.com',
  isExternal: true, // 새 창 아이콘 자동 표시
  child: const Text('외부 리소스'),
)

외부 링크 아이콘이 사용자에게 새 탭이 열릴 것임을 미리 알려 혼란을 방지한다.


❌ Don't#

링크 텍스트로 "여기", "클릭" 같은 모호한 표현 사용

// ❌ 맥락 없는 링크 텍스트
CouiLink(
  onTap: handleNavigate,
  child: const Text('여기를 클릭하세요'),
)

스크린 리더 사용자는 링크 텍스트만 읽어 탐색하므로 "여기"는 의미가 없다. 목적지를 명확히 서술한다.

✅ Do#

링크 텍스트는 목적지를 설명하는 내용으로 작성하세요.

// ✅ 명확한 링크 텍스트
CouiLink(
  href: '/docs/getting-started',
  child: Text('시작 가이드 보기'),
)

링크 텍스트가 목적지를 설명하면 사용자와 스크린 리더가 클릭 전에 무엇으로 이동하는지 파악할 수 있습니다.


❌ Don't#

'여기', '클릭' 같은 모호한 링크 텍스트를 사용하지 마세요.

// ❌ 모호한 링크 텍스트
CouiLink(href: '/docs', child: Text('여기'))
CouiLink(href: '/contact', child: Text('클릭하세요'))

스크린 리더는 링크 텍스트만 읽어줍니다. '여기', '클릭'은 어디로 이동하는지 알 수 없어 접근성을 심각하게 해칩니다.

접근성 (Accessibility)#

키보드 인터랙션#

동작
Tab링크로 포커스 이동
Enter링크 활성화

스크린 리더#

  • Flutter: Semantics(link: true, label: linkText) 자동 적용
  • Web: <a> 태그로 렌더링; href 있으면 자동 링크 역할; isExternal이면 rel="noopener noreferrer" + target="_blank" 자동

터치 타겟#

  • 인라인 링크의 경우 부모 컨테이너 크기에 따름
  • 독립 링크는 최소 44x44dp 터치 영역 확보 권장

크로스 플랫폼 차이점 (Platform Differences)#

항목FlutterWeb
클래스명CouiLinkLink
내부 라우팅onTap + Navigatorhref 속성 (SPA 라우터 처리)
외부 링크url_launcher 패키지target="_blank"
방문 색상지원 안 함CSS :visited
  • Button: 탐색이 아닌 액션 실행에 사용
  • Text: 링크가 없는 일반 텍스트에 사용
  • Breadcrumb: 계층적 페이지 탐색 경로 표시에 사용

조합 예제#

// 로그인 폼 하단 링크들
Column(
  children: [
    // ... 폼 필드들
    Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        CouiLink(
          onTap: handleForgotPassword,
          variant: LinkVariant.muted,
          child: const Text('비밀번호를 잊으셨나요?'),
        ),
        CouiLink(
          onTap: handleSignUp,
          variant: LinkVariant.underline,
          child: const Text('회원가입'),
        ),
      ],
    ),
    const Gap.md(),
    CouiButton(
      onPressed: handleLogin,
      child: const Text('로그인'),
    ),
    const Gap.sm(),
    Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        const Text('계속 진행하면 '),
        CouiLink(
          href: '/terms',
          variant: LinkVariant.muted,
          child: const Text('이용약관'),
        ),
        const Text('에 동의하는 것입니다.'),
      ],
    ),
  ],
)