Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,30 @@
//

import SwiftUI
import CoreDesignSystem
import UMCFoundation

extension UMCPartType {

/// ์šฐ๋ฆฌ๊ฐ€ ๋ชป ์ฝ์€ ํŒŒํŠธ(`MyCard.partRaw` ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ)์˜ ์‹œ๋“œ ํด๋ฐฑ.
///
/// `partRaw != nil` ์ด๋ฉด `part` ๊ฐ€ ๊ด€๋ก€์ƒ `.admin` ์œผ๋กœ ๋–จ์–ด์ ธ Admin ์ธ๋””๊ณ (#6155F5)๋กœ
/// ์น ํ•ด์ง€๋˜ ๋ฒ„๊ทธ๋ฅผ ๋ง‰๋Š”๋‹ค. ์ฑ„๋„ํญ 13 ์˜ ์ €์ฑ„๋„ ํšŒ์ƒ‰์ด๋ผ ์œ ์ฑ„์ƒ‰ ์‹œ๋“œ 8์ข…(์ฑ„๋„ํญ ์ตœ์†Œ 78)
/// ๊ณผ ์„ž์ด์ง€ ์•Š๋Š”๋‹ค (#1236 ํ™•์ •).
///
/// - Note: ์‹œ๋“œ 8์ข…๊ณผ ๋‹ฌ๋ฆฌ ์ด ๊ฐ’๋งŒ ๋‹ค์ด๋‚ด๋ฏน ํ† ํฐ์ด๋ผ ๋ชจ๋“œ์— ๋”ฐ๋ผ ๋ณ€ํ•œ๋‹ค
/// (light `#B2B8BF` / dark `#7C8792`). ์นฉ ๋ฉด์ด ๋ชจ๋“œ ๋ถˆ๋ณ€์ด๋ผ๋Š” ์ „์ œ์˜ ์œ ์ผํ•œ ์˜ˆ์™ธ์ง€๋งŒ,
/// ๊ฒ€์ • ๋ผ๋ฒจ ๋Œ€๋น„๊ฐ€ ๋ผ์ดํŠธ 12.22 ยท ๋‹คํฌ 7.82 ๋กœ ์–‘์ชฝ ๋‹ค AA ๋ฅผ ๋„˜์–ด ์ž‰ํฌ๋Š” ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค.
public static let unresolvedSeedColor: Color = .grey400

/// ``unresolvedSeedColor`` ์˜ ์นฉ ๋ฉด โ€” ์‹œ๋“œ 8์ข…๊ณผ ๊ฐ™์€ ํ˜ผํ•ฉ ๊ทœ์น™(``chipSeedColor``)์„ ํƒ„๋‹ค.
public static var unresolvedChipSeedColor: Color {
unresolvedSeedColor.mix(with: .white, by: chipWhiteMix, in: .device)
}

/// ์นฉ ๋ฉด์„ ๋งŒ๋“ค ๋•Œ ์‹œ๋“œ์— ์„ž๋Š” ํฐ์ƒ‰ ๋น„์œจ. ํด๋ฐฑ๊นŒ์ง€ ๊ฐ™์€ ๊ทœ์น™์„ ํƒ€์•ผ ํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ ํ•œ ๊ณณ์— ๋‘”๋‹ค.
private static let chipWhiteMix: Double = 0.2

/// ๋ช…ํ•จ ์‹œ๋“œ ์ปฌ๋Ÿฌ โ€” ์‹œ์•ˆ์ด raw hex ๋กœ ์ง€์ •ํ•œ ํŒŒํŠธ ๋ธŒ๋žœ๋“œ ์ƒ‰.
///
/// ``color`` ์™€ **์ผ๋ถ€๋Ÿฌ ๋ถ„๋ฆฌํ•ด ๋‘”๋‹ค.** ``color`` ๋Š” SwiftUI ์‹œ์Šคํ…œ ์ปฌ๋Ÿฌ๋ผ
Expand All @@ -21,7 +41,8 @@ extension UMCPartType {
///
/// - Note: iOS ํŒŒํŠธ๋งŒ ์‹œ์•ˆ์— ๋ณ€ํ˜•์ด ์—†์–ด(7์ข…) `systemOrange` ๋กœ ์ฑ„์› ๋‹ค.
/// ๋‚˜๋จธ์ง€ 7์ข…์ด iOS ์‹œ์Šคํ…œ ํŒ”๋ ˆํŠธ ๊ณ„์—ด์ด๊ณ  ์ฃผํ™ฉ์ด ๋น„์–ด ์žˆ์–ด ์ถฉ๋Œํ•˜์ง€ ์•Š๋Š”๋‹ค.
/// ๋””์ž์ด๋„ˆ ํ™•์ธ์ด ์˜ค๋ฉด ์ด ๊ฐ’๋งŒ ๊ต์ฒดํ•˜๋ฉด ๋œ๋‹ค.
/// #1236 ์—์„œ ํ˜„ํ–‰ ์œ ์ง€๋กœ ํ™•์ •ํ–ˆ๋‹ค. ๊ณต์‹ iOS ๋ณ€ํ˜• ์ƒ‰์ด ์˜ค๋ฉด ์ด ํ•œ ๊ฐ’๊ณผ
/// `UMCPartTypeSeedColorTests` ๋งŒ ๊ต์ฒดํ•œ๋‹ค.
public var seedColor: Color {
switch self {
case .admin:
Expand Down Expand Up @@ -49,6 +70,19 @@ extension UMCPartType {
}
}

/// ๋ช…ํ•จ_s ํŒŒํŠธยท๊ธฐ์ˆ˜ ์นฉ์˜ **๋ฉด** ์ƒ‰ โ€” ์‹œ๋“œ์— ํฐ์ƒ‰์„ ์„ž์–ด ๊ตณํžŒ ๋ถˆํˆฌ๋ช…์ƒ‰.
///
/// ์•ŒํŒŒ ํ•ฉ์„ฑ(์‹œ๋“œ@0.8)์„ ์“ฐ์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ผ์ดํŠธยท๋‹คํฌ์—์„œ ๊ฐ™์€ ์ƒ‰์œผ๋กœ ๋‚จ๊ธฐ๊ธฐ ์œ„ํ•ด์„œ๋‹ค.
/// ์นฉ ๋ผ๋ฒจ์ด ๋ชจ๋“œ ๋ถˆ๋ณ€์ธ `Color.black` ๊ณ ์ •์ด๋ผ ๋ฉด๋„ ๋ชจ๋“œ ๋ถˆ๋ณ€์ด์–ด์•ผ ํ•œ๋‹ค โ€” ๋ผ๋ฒจ ์ƒ‰๋งŒ
/// ๋ฐ”๊พธ๋Š” ์•ˆ์€ ๋‹คํฌ์—์„œ ๋‹ค์‹œ ๊นจ์ง„๋‹ค(๊ฒ€์ • ๋ผ๋ฒจ: Admin 2.95ยทPM 3.47ยทDesign 3.90ยทWeb 4.08
/// ๋ฏธ๋‹ฌ / ํฐ ๋ผ๋ฒจ: Spring 3.42ยทAndroid 3.32ยทiOS 3.38ยทNode 2.39 ๋ฏธ๋‹ฌ, #1235 ์‹ค์ธก).
///
/// ์ด ๊ฐ’ ์œ„์˜ ๊ฒ€์ • ๋ผ๋ฒจ์€ 8์ข… ์ „๋ถ€ **5.92~14.92** ๋กœ WCAG AA(4.5:1)๋ฅผ ํ†ต๊ณผํ•œ๋‹ค โ€”
/// `UMCPartTypeSeedColorTests` ๊ฐ€ ๊ณ ์ •ํ•œ๋‹ค (#1236 ํ™•์ •).
public var chipSeedColor: Color {
seedColor.mix(with: .white, by: Self.chipWhiteMix, in: .device)
}

/// ํŒŒํŠธ๋ณ„ ๊ณ ์œ  ์ƒ‰์ƒ
public var color: Color {
switch self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ struct UMCPartTypeSeedColorTests {
(.server(type: .node), "FFCC00"),
]

/// ํšŒ์ƒ‰๊ณผ ์œ ์ฑ„์ƒ‰์„ ๊ฐ€๋ฅด๋Š” ์ฑ„๋„ํญ ๊ฒฝ๊ณ„. ์‹ค์ธก์€ ํด๋ฐฑ 13(๋ผ์ดํŠธ)ยท22(๋‹คํฌ) ๋Œ€ ์‹œ๋“œ ์ตœ์†Œ
/// 78(Web) ์ด๋ผ ๊ทธ ์‚ฌ์ด ์–ด๋””๋ฅผ ์žก์•„๋„ ๋˜๊ณ , ์–‘์ชฝ์— ๋„‰๋„‰ํ•œ 40 ์œผ๋กœ ๋‘”๋‹ค.
private static let greyChannelSpreadLimit = 40

/// WCAG AA ๋ณธ๋ฌธ ๊ธฐ์ค€.
private static let minimumContrastRatio = 4.5

@Test("ํŒŒํŠธ๋งˆ๋‹ค ์‹œ์•ˆ์ด ์ง€์ •ํ•œ ์‹œ๋“œ ์ปฌ๋Ÿฌ๋ฅผ ๋Œ๋ ค์ค€๋‹ค", arguments: designSeeds)
func seedColorMatchesDesign(seed: (part: UMCPartType, hex: String)) {
#expect(seed.part.seedColor.srgbHex == seed.hex)
Expand All @@ -39,6 +46,48 @@ struct UMCPartTypeSeedColorTests {
#expect(Set(hexes).count == Self.designSeeds.count)
}

/// ๋ชป ์ฝ์€ ํŒŒํŠธ๋Š” `.admin` ์œผ๋กœ ๋–จ์–ด์ ธ Admin ์ธ๋””๊ณ ๋ฅผ ์ž…๋˜ ์ž๋ฆฌ๋‹ค (#1236). hex ์™„์ „
/// ์ผ์น˜๋งŒ ๋ณด๋ฉด ํด๋ฐฑ์„ `6155F6`(Admin ์—์„œ 1๋น„ํŠธ) ๋กœ ์˜ฎ๊ฒจ๋„ ํ†ต๊ณผํ•˜๋ฉด์„œ ใ€Œ์šด์˜์ง„์ฒ˜๋Ÿผ
/// ๋ณด์ž„ใ€์ด ๋˜์‚ด์•„๋‚˜๋ฏ€๋กœ, **ํšŒ์ƒ‰์ด๋ผ๋Š” ์„ฑ์งˆ ์ž์ฒด**๋ฅผ ์ฑ„๋„ํญ์œผ๋กœ ๋ชป ๋ฐ•๋Š”๋‹ค.
@Test("๋ฏธ์ •์˜ ํŒŒํŠธ ํด๋ฐฑ์€ ์ €์ฑ„๋„ ํšŒ์ƒ‰์ด๋ผ ์œ ์ฑ„์ƒ‰ ์‹œ๋“œ์™€ ์„ž์ด์ง€ ์•Š๋Š”๋‹ค")
func unresolvedSeedIsLowChromaGrey() {
let fallback = UMCPartType.unresolvedSeedColor
let hexes = Set(Self.designSeeds.map(\.part.seedColor.srgbHex))

#expect(!hexes.contains(fallback.srgbHex))
#expect(
fallback.channelSpread < Self.greyChannelSpreadLimit,
"ํด๋ฐฑ ์ฑ„๋„ํญ \(fallback.channelSpread)"
)

for seed in Self.designSeeds {
#expect(
seed.part.seedColor.channelSpread > Self.greyChannelSpreadLimit,
"\(seed.part.name) ์ฑ„๋„ํญ \(seed.part.seedColor.channelSpread)"
)
}
}

/// ์ด ๊ฐ’์ด ํฐ ๋ผ๋ฒจ(#1235, 8์ข… ์ „๋ถ€ ๋ฏธ๋‹ฌ)์„ ๋ฒ„๋ฆฌ๊ณ  ๊ฒ€์ • ๋ผ๋ฒจ๋กœ ๊ฐ„ ๊ทผ๊ฑฐ๋‹ค. ์‹œ๋“œ๋‚˜
/// ํ˜ผํ•ฉ๋น„๋ฅผ ๊ฑด๋“œ๋ฆฌ๋ฉด ํ™”๋ฉด์ด ์•„๋‹ˆ๋ผ ์—ฌ๊ธฐ์„œ ๋จผ์ € ๊นจ์ ธ์•ผ ํ•œ๋‹ค.
@Test(
"ํŒŒํŠธ ์นฉ ๋ฉด ์œ„ ๊ฒ€์ • ๋ผ๋ฒจ์ด ์—ฌ๋Ÿ ํŒŒํŠธ ์ „๋ถ€ WCAG AA(4.5:1)๋ฅผ ๋„˜๋Š”๋‹ค",
arguments: designSeeds
)
func chipSeedColorPassesBlackLabelContrast(seed: (part: UMCPartType, hex: String)) {
let ratio = seed.part.chipSeedColor.blackContrastRatio

#expect(ratio >= Self.minimumContrastRatio, "\(seed.part.name) ๋Œ€๋น„ \(ratio)")
}

/// ํด๋ฐฑ๋งŒ ๋‹ค์ด๋‚ด๋ฏน ํ† ํฐ์ด๋ผ ๋ชจ๋“œ์— ๋”ฐ๋ผ ๋ณ€ํ•œ๋‹ค โ€” ์–ด๋А ๋ชจ๋“œ๋กœ ํ•ด์„๋˜๋“  ๊ธฐ์ค€์„ ๋„˜๋Š”๋‹ค.
@Test("๋ชป ์ฝ์€ ํŒŒํŠธ ํด๋ฐฑ ์นฉ ๋ฉด ์œ„ ๊ฒ€์ • ๋ผ๋ฒจ๋„ ๊ฐ™์€ ๊ธฐ์ค€์„ ๋„˜๋Š”๋‹ค")
func unresolvedChipSeedColorPassesBlackLabelContrast() {
let ratio = UMCPartType.unresolvedChipSeedColor.blackContrastRatio

#expect(ratio >= Self.minimumContrastRatio, "ํด๋ฐฑ ๋Œ€๋น„ \(ratio)")
}

/// `allCases` ๋Š” `.admin` ์„ ๋นผ๊ณ  7์ข…๋งŒ ๋‹ด๋Š”๋‹ค(์šด์˜์ง„์€ ํŒŒํŠธ ์„ ํƒ ๋ชฉ๋ก์— ์•ˆ ์˜ค๋ฅธ๋‹ค).
/// ๋ช…ํ•จ์€ ์šด์˜์ง„ ์นด๋“œ๋„ ๊ทธ๋ ค์•ผ ํ•˜๋ฏ€๋กœ ๊ทธ 7์ข…์„ ๋ฎ๋˜ admin ๊นŒ์ง€ ๋ณ„๋„๋กœ ํ™•์ธํ•œ๋‹ค.
@Test("ํŒŒํŠธ ์„ ํƒ ๋ชฉ๋ก(allCases) ์ „์ฒด๊ฐ€ ์‹œ๋“œ ์ปฌ๋Ÿฌ๋ฅผ ๊ฐ–๋Š”๋‹ค")
Expand All @@ -54,18 +103,48 @@ struct UMCPartTypeSeedColorTests {

private extension Color {

/// sRGB 8bit ํ‘œ๊ธฐ. ์‹œ์•ˆ hex ๋ฌธ์ž์—ด๊ณผ ์ง์ ‘ ๋งž๋Œ€๋ ค๊ณ  ํ…Œ์ŠคํŠธ์—์„œ๋งŒ ์“ด๋‹ค.
var srgbHex: String {
/// sRGB 8bit ์ฑ„๋„. hex ๋น„๊ตยท์ฑ„๋„ํญยท๋Œ€๋น„ ๊ณ„์‚ฐ์ด ๋ชจ๋‘ ์ด ํ•œ ๊ฐ’์—์„œ ๋‚˜์˜จ๋‹ค.
var srgbChannels: (red: Int, green: Int, blue: Int) {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
UIColor(self).getRed(&red, green: &green, blue: &blue, alpha: &alpha)
return String(
format: "%02X%02X%02X",
return (
Int((red * 255).rounded()),
Int((green * 255).rounded()),
Int((blue * 255).rounded())
)
}

/// sRGB 8bit ํ‘œ๊ธฐ. ์‹œ์•ˆ hex ๋ฌธ์ž์—ด๊ณผ ์ง์ ‘ ๋งž๋Œ€๋ ค๊ณ  ํ…Œ์ŠคํŠธ์—์„œ๋งŒ ์“ด๋‹ค.
var srgbHex: String {
let channels = srgbChannels
return String(format: "%02X%02X%02X", channels.red, channels.green, channels.blue)
}

/// ์ตœ๋Œ€ ์ฑ„๋„๊ณผ ์ตœ์†Œ ์ฑ„๋„์˜ ์ฐจ. ๋ฌด์ฑ„์ƒ‰์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก 0 ์— ๋ถ™๋Š”๋‹ค.
var channelSpread: Int {
let channels = [srgbChannels.red, srgbChannels.green, srgbChannels.blue]
return (channels.max() ?? 0) - (channels.min() ?? 0)
}

/// WCAG 2.1 ์ƒ๋Œ€ ํœ˜๋„.
var relativeLuminance: Double {
let channels = srgbChannels
func linear(_ value: Int) -> Double {
let normalized = Double(value) / 255
return normalized <= 0.03928
? normalized / 12.92
: pow((normalized + 0.055) / 1.055, 2.4)
}
return 0.2126 * linear(channels.red)
+ 0.7152 * linear(channels.green)
+ 0.0722 * linear(channels.blue)
}

/// ๊ฒ€์ • ์ž‰ํฌ์™€์˜ ๋Œ€๋น„๋น„ โ€” ๊ฒ€์ • ํœ˜๋„๊ฐ€ 0 ์ด๋ผ `(L + 0.05) / 0.05` ๋กœ ์ค„์–ด๋“ ๋‹ค.
var blackContrastRatio: Double {
(relativeLuminance + 0.05) / 0.05
}
}
10 changes: 10 additions & 0 deletions UMCApp/Features/BusinessCard/Domain/Sources/Models/MyCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ public struct MyCard: Equatable, Hashable, Sendable {

// MARK: - Computed Property

/// ์นด๋“œ์— ์‹ฃ๋Š” ํ‘œ์‹œ ์ด๋ฆ„ โ€” ์‹œ์•ˆ ๋”๋ฏธ `์ด๋ฆ„/๋‹‰๋„ค์ž„` ์„ ๊ทธ๋Œ€๋กœ ๋”ฐ๋ฅธ๋‹ค.
///
/// ๋ช…ํ•จ_l(`12639:33234`)ยท๋ช…ํ•จ_m(`12639:33316`)ยท๋ช…ํ•จ_s(`12657:35806`) ๊ณตํ†ต ๊ทœ์น™์ด๊ณ 
/// ๋”๋ฏธ๊ฐ€ ์ฒ˜์Œ ๋‚˜์˜จ ์›์ถœ์ฒ˜๋Š” ๋ช…ํ•จ_s ๋‹ค. ๋‹‰๋„ค์ž„์ด ๊ณต๋ฐฑ๋ฟ์ด๋ฉด ์ด๋ฆ„๋งŒ ์‹ฃ๋Š”๋‹ค. ํ‘œ๊ธฐ๊ฐ€
/// ๋ฐ”๋€Œ๋ฉด ์ด ํ•œ ๊ณณ๋งŒ ๊ณ ์นœ๋‹ค (#1236 ํ™•์ •).
public var nameWithNickname: String {
let trimmed = nickname.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? name : "\(name)/\(trimmed)"
}

/// ํ™”๋ฉด์— ์“ฐ๋Š” ํŒŒํŠธ ์ด๋ฆ„. ๋ชป ์ฝ์€ ๊ฐ’์ด๋ฉด ์›๋ณธ์„ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ค€๋‹ค โ€” ํ‹€๋ฆฐ ์ด๋ฆ„(์šด์˜์ง„)๋ณด๋‹ค
/// ๋‚ฏ์„  ์ด๋ฆ„์ด ๋‚ซ๋‹ค.
public var partDisplayName: String {
Expand Down
28 changes: 28 additions & 0 deletions UMCApp/Features/BusinessCard/Domain/Tests/MyCardTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@ import UMCFoundation
@Suite("MyCard โ€” ํŒŒ์ƒ ํ”„๋กœํผํ‹ฐ")
struct MyCardTests {

/// ๋ช…ํ•จ_lยท_mยท_s ์„ธ ์นด๋“œ๊ฐ€ ์ด ํ•œ ๊ทœ์น™์„ ๊ณต์œ ํ•œ๋‹ค (#1236). ์ž˜๋ฆผ์€ ๋ทฐ๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ
/// ์—ฌ๊ธฐ์„œ๋Š” ๊ธธ์ด๋ฅผ ์ž๋ฅด์ง€ ์•Š๊ณ  ์›๋ฌธ์„ ๊ทธ๋Œ€๋กœ ์ž‡๋Š”์ง€๋งŒ ๋ณธ๋‹ค.
///
/// ๋งˆ์ง€๋ง‰ ์ค„์€ ์ด๋ฆ„์ด ๋นˆ ํ‡ดํ™” ์ƒํƒœ์˜ **ํ˜„์žฌ ๋™์ž‘**์„ ์ ์–ด ๋‘” ๊ฒƒ์ด๋‹ค โ€” ์„œ๋ฒ„๊ฐ€ ์ด๋ฆ„์„
/// ์•ˆ ์ฃผ๋Š” ์นด๋“œ๋Š” ``MyCard/validate()`` ๊ฐ€ ์•„๋‹ˆ๋ผ ์—ฌ๊ธฐ์„œ `/๋‹‰๋„ค์ž„` ์œผ๋กœ ์ƒˆ ๋‚˜๊ฐ„๋‹ค.
private static let nameCases: [(name: String, nickname: String, expected: String)] = [
(name: "๊น€๋ฏผ", nickname: "๋ฏผ", expected: "๊น€๋ฏผ/๋ฏผ"),
(name: "๋‚จ๊ถ์ˆ˜ํ•˜๋Š˜๋ณด๋ผ๋งค", nickname: "๊ตฌ๋ฆ„", expected: "๋‚จ๊ถ์ˆ˜ํ•˜๋Š˜๋ณด๋ผ๋งค/๊ตฌ๋ฆ„"),
(name: "Christopher Nolan", nickname: "Chris", expected: "Christopher Nolan/Chris"),
(name: "๋ฐ•์„œ์ค€", nickname: "", expected: "๋ฐ•์„œ์ค€"),
(name: "๋ฐ•์„œ์ค€", nickname: " ", expected: "๋ฐ•์„œ์ค€"),
(name: "๊น€๋ฏผ", nickname: " ๋ฏผ ", expected: "๊น€๋ฏผ/๋ฏผ"),
(name: "", nickname: "๋ฏผ", expected: "/๋ฏผ"),
]

@Test("qrPayload๋Š” cardLink urlString๊ณผ ํ•ญ์ƒ ๊ฐ™๋‹ค (MP-F02ยทF04 ๋™์ผ ํŽ˜์ด๋กœ๋“œ)")
func qrPayloadEqualsCardLink() {
let card = MyCard(
Expand All @@ -24,4 +39,17 @@ struct MyCardTests {
#expect(card.qrPayload == CardLink(memberId: "42").urlString)
#expect(card.qrPayload == card.cardLink.urlString)
}

@Test("๋‹‰๋„ค์ž„์ด ์žˆ์œผ๋ฉด `์ด๋ฆ„/๋‹‰๋„ค์ž„`, ๊ณต๋ฐฑ๋ฟ์ด๋ฉด ์ด๋ฆ„๋งŒ ์‹ฃ๋Š”๋‹ค", arguments: nameCases)
func nameWithNicknameFollowsDesignDummy(
nameCase: (name: String, nickname: String, expected: String)
) {
let card = MyCard(
memberId: "42", name: nameCase.name, nickname: nameCase.nickname,
part: .front(type: .ios), generation: "12", university: "ํ•œ์–‘๋Œ€ํ•™๊ต",
email: nil, github: nil, linkedIn: nil, blog: nil, avatarURL: nil
)

#expect(card.nameWithNickname == nameCase.expected)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,22 +182,20 @@ public struct BusinessCardFaceView: View {
.accessibilityLabel(isFlipped ? Constants.flipToFront : Constants.flipToBack)
}

/// ์‹œ์•ˆ ๋”๋ฏธ `์ด๋ฆ„/๋‹‰๋„ค์ž„` ๊ทœ์น™ โ€” ๋‹‰๋„ค์ž„์ด ๋น„์–ด ์žˆ์œผ๋ฉด ์ด๋ฆ„๋งŒ ์‹ฃ๋Š”๋‹ค
/// (๋ช…ํ•จ_mยท๋ช…ํ•จ_s ์™€ ๊ฐ™์€ ๊ทœ์น™).
private var displayName: String {
let nickname = card.nickname.trimmingCharacters(in: .whitespacesAndNewlines)
return nickname.isEmpty ? card.name : "\(card.name)/\(nickname)"
}

private var frontFace: some View {
HStack(alignment: .top, spacing: Metrics.contentSpacing) {
avatar

VStack(alignment: .leading, spacing: Metrics.contentSpacing) {
HStack(alignment: .lastTextBaseline, spacing: Metrics.nameSpacing) {
Text(displayName)
// ํ‘œ๊ธฐ ๊ทœ์น™์€ ``MyCard/nameWithNickname`` ํ•œ ๊ณณ์— ์žˆ๋‹ค โ€” ๋ช…ํ•จ_lยท_mยท_s
// ๊ณตํ†ต์ด๊ณ  ์›์ถœ์ฒ˜๋Š” ๋ช…ํ•จ_s ์‹œ์•ˆ(`12657:35806`)์ด๋‹ค.
// ๋‘˜ ๋‹ค `lineLimit(1)` ์ด๋ผ ํญ์ด ๋ชจ์ž๋ผ๋ฉด ์–ด๋А ์ชฝ์ด ์ž˜๋ฆด์ง€ ๊ทœ์น™์ด
// ์—†์—ˆ๋‹ค. ์ด๋ฆ„์ด 1์ฐจ ์‹๋ณ„์ž๋ผ ํ•™๊ต๊ฐ€ ๋จผ์ € ๋ง์ค„์ž„๋˜๊ฒŒ ๋ชป ๋ฐ•๋Š”๋‹ค (#1236).
Text(card.nameWithNickname)
.appFont(.title3, weight: .semibold, color: Color.white)
.lineLimit(1)
.layoutPriority(1)

Text(card.university)
.appFont(.footnote, color: Color.white)
Expand All @@ -219,8 +217,12 @@ public struct BusinessCardFaceView: View {

/// ใ€Œํ™๊ธธ๋™/๊ธธ๋™, โ—‹โ—‹๋Œ€ํ•™๊ต, iOS ํŒŒํŠธ, 12๊ธฐใ€.
private var frontAccessibilityLabel: String {
[displayName, card.university, "\(card.partDisplayName) ํŒŒํŠธ", "\(card.generation)๊ธฐ"]
.joined(separator: ", ")
[
card.nameWithNickname,
card.university,
"\(card.partDisplayName) ํŒŒํŠธ",
"\(card.generation)๊ธฐ",
].joined(separator: ", ")
}

/// ๋’ท๋ฉด์€ ์•„๋ฐ”ํƒ€ ์ž๋ฆฌ์— QR ์ด ์˜ค๊ณ , ์ด๋ฆ„ยท์นฉ ์ž๋ฆฌ์— ๋งํฌ 3์ค„์ด ์˜จ๋‹ค.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,27 +93,30 @@ struct BusinessCardSummaryView: View {

/// ใ€Œํ™๊ธธ๋™/๊ธธ๋™, โ—‹โ—‹๋Œ€ํ•™๊ต, iOS ํŒŒํŠธ, 12๊ธฐใ€.
private var accessibilityLabel: String {
[displayName, card.university, "\(card.partDisplayName) ํŒŒํŠธ", "\(card.generation)๊ธฐ"]
.joined(separator: ", ")
[
card.nameWithNickname,
card.university,
"\(card.partDisplayName) ํŒŒํŠธ",
"\(card.generation)๊ธฐ",
].joined(separator: ", ")
}

// MARK: - View Component

/// ์‹œ์•ˆ ๋”๋ฏธ๊ฐ€ `์ด๋ฆ„/๋‹‰๋„ค์ž„` ์ด๋ผ ๋ช…ํ•จ_l(``BusinessCardFaceView``)๊ณผ ๊ฐ™์€ ๊ทœ์น™์œผ๋กœ
/// ๋‘˜์„ ํ•จ๊ป˜ ์‹ฃ๋Š”๋‹ค. ๋‹‰๋„ค์ž„์ด ๋น„์–ด ์žˆ์œผ๋ฉด ์ด๋ฆ„๋งŒ.
private var displayName: String {
let nickname = card.nickname.trimmingCharacters(in: .whitespacesAndNewlines)
return nickname.isEmpty ? card.name : "\(card.name)/\(nickname)"
}

private var nameRow: some View {
HStack(alignment: .firstTextBaseline, spacing: Metrics.nameSpacing) {
// ์‹œ์•ˆ์€ Pretendard Bold ์ง€๋งŒ `AppFontWeight` ์— bold ๊ฐ€ ์—†๋‹ค(regular/medium/
// semibold 3์ข…). ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด semibold ๋ฅผ ์“ด๋‹ค โ€” ๋ช…ํ•จ_l ๋„ ๊ฐ™์€ ์ฒ˜๋ฆฌ๋ฅผ ํ–ˆ๋‹ค.
// ํ™•์žฅ์€ `Pretendard-Bold.otf` ๋ฐ˜์ž…์ด ์„ ํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค (#1237).
Text(displayName)
//
// ํ‘œ๊ธฐ ๊ทœ์น™์€ ``MyCard/nameWithNickname`` ํ•œ ๊ณณ์— ์žˆ๋‹ค โ€” ๋ช…ํ•จ_lยท_mยท_s
// ๊ณตํ†ต์ด๊ณ  ์›์ถœ์ฒ˜๋Š” ๋ช…ํ•จ_s ์‹œ์•ˆ(`12657:35806`)์ด๋‹ค.
// ๋‘˜ ๋‹ค `lineLimit(1)` ์ด๋ผ ํญ์ด ๋ชจ์ž๋ผ๋ฉด ์–ด๋А ์ชฝ์ด ์ž˜๋ฆด์ง€ ๊ทœ์น™์ด ์—†์—ˆ๋‹ค.
// ์ด๋ฆ„์ด 1์ฐจ ์‹๋ณ„์ž๋ผ ํ•™๊ต๊ฐ€ ๋จผ์ € ๋ง์ค„์ž„๋˜๊ฒŒ ๋ชป ๋ฐ•๋Š”๋‹ค (#1236).
Text(card.nameWithNickname)
.appFont(.title2, weight: .semibold, color: Color.white)
.lineLimit(1)
.layoutPriority(1)

Text(card.university)
.appFont(.subheadline, color: Color.white)
Expand Down
Loading
Loading