๐ keyof ์ฐ์ฐ์: ๊ฐ์ฒด ์์ฑ์ ํ์ ์ผ๋ก ํ์ฉํ๊ธฐ ๐

์๋ ํ์ธ์, ์ฌ๋ฌ๋ถ! ์ค๋์ TypeScript์ ๊ฟํ ์ค ํ๋์ธ 'keyof ์ฐ์ฐ์'์ ๋ํด ์์๋ณผ ๊ฑฐ์์. ์ด ๋ ์, ๊ฐ์ฒด ์์ฑ์ ํ์ ์ ๋ค๋ฃจ๋ ๋ฐ ์์ด์ ์ง์ง ๋๋ฐ ์ ์ฉํ๋ต๋๋ค! ๐
TypeScript๋ฅผ ์ฒ์ ์ ํ์๋ ๋ถ๋ค๋ ์์ ํ ๋, ์ฐ์ ๊ฐ๋จํ ์ค๋ช ๋๋ฆด๊ฒ์. TypeScript๋ JavaScript์ ์ํผ์ ์ธ์ด๋ก, ํ์ ์์คํ ์ ์ถ๊ฐํด ์ฝ๋์ ์์ ์ฑ๊ณผ ๊ฐ๋ ์ฑ์ ๋์ฌ์ฃผ๋ ๋ ์์ด์์. ๊ทธ ์ค์์๋ 'keyof' ์ฐ์ฐ์๋ ๊ฐ์ฒด ํ์ ์ ๋ชจ๋ ํค๋ฅผ ๋ฌธ์์ด ๋๋ ์ซ์ ๋ฆฌํฐ๋ด ์ ๋์จ์ผ๋ก ์์ฑํ๋ ํน๋ณํ ๋ ์์ด๋๋๋ค.
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก 'keyof'์ ๋ํด ํํค์ณ๋ณผ๊น์? ์ค๋น๋์ จ๋์? ๊ทธ๋ผ ๊ณ ๊ณ ์ฝ~ ๐
1. keyof์ ๊ธฐ๋ณธ ๊ฐ๋ ๐ง
'keyof'๋ TypeScript์์ ์ ๊ณตํ๋ ํน๋ณํ ์ฐ์ฐ์์์. ์ด ๋ ์์ ์ฃผ์ ์๋ฌด๋ ๋ญ๋๊ณ ์? ๋ฐ๋ก ๊ฐ์ฒด ํ์ ์์ ํค๋ค์ ์ ๋์จ ํ์ ์ ๋ง๋ค์ด๋ด๋ ๊ฑฐ๋๋๋ค! ์ด๋ค ๋๋์ธ์ง ์์ ๋ก ํ๋ฒ ๋ณผ๊น์?
interface Person {
name: string;
age: number;
location: string;
}
type PersonKeys = keyof Person; // "name" | "age" | "location"
์ ์ฝ๋์์ 'PersonKeys'๋ "name", "age", "location"์ ์ ๋์จ ํ์ ์ด ๋๋ ๊ฑฐ์ฃ . ์ด๋ ๊ฒ ํ๋ฉด Person ๊ฐ์ฒด์ ๋ชจ๋ ํค๋ฅผ ํ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ๋๋ ๊ฑฐ์์. ์ ๊ธฐํ์ฃ ? ๐ฎ
์ด๋ฐ ์์ผ๋ก 'keyof'๋ฅผ ์ฌ์ฉํ๋ฉด, ๊ฐ์ฒด์ ์์ฑ์ ํ์ ์์ ํ๊ฒ ์ ๊ทผํ ์ ์์ด์. ์๋ฅผ ๋ค์ด, ํจ์์์ ๊ฐ์ฒด์ ํน์ ํค์ ์ ๊ทผํด์ผ ํ ๋ ์ ์ฉํ๊ฒ ์ธ ์ ์๋ต๋๋ค.
function getProperty<t k extends keyof t>(obj: T, key: K): T[K] {
return obj[key];
}
const person: Person = {
name: "Alice",
age: 30,
location: "New York"
};
const name = getProperty(person, "name"); // ํ์
: string
const age = getProperty(person, "age"); // ํ์
: number
const location = getProperty(person, "location"); // ํ์
: string
</t>
์ด ์์ ์์ 'getProperty' ํจ์๋ ๊ฐ์ฒด์ ํค๋ฅผ ๋ฐ์์ ํด๋น ํค์ ๊ฐ์ ๋ฐํํด์. 'K extends keyof T'๋ผ๋ ์ ์ฝ ์กฐ๊ฑด ๋๋ถ์, ์กด์ฌํ์ง ์๋ ํค๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ๋ต๋๋ค. ์์ ํ๊ณ ํธ๋ฆฌํ์ฃ ? ๐
๐ Pro Tip: 'keyof'๋ ๋จ์ํ ํค๋ฅผ ๋์ดํ๋ ๊ฒ ์ด์์ ๊ธฐ๋ฅ์ ์ ๊ณตํด์. ๊ฐ์ฒด์ ๊ตฌ์กฐ๋ฅผ ํ์ ์์คํ ์ ๋ฐ์ํจ์ผ๋ก์จ, ํ์ ์์ ์ฑ์ ๋์ด๊ณ ์๋ ์์ฑ ๊ธฐ๋ฅ์ ๊ฐ์ ํ๋ ๋ฐ ํฐ ๋์์ ์ค๋ต๋๋ค!
์ด๋ ๊ฒ 'keyof'์ ๊ธฐ๋ณธ ๊ฐ๋ ์ ๋ํด ์์๋ดค์ด์. ์ด๋์? ์๊ฐ๋ณด๋ค ์ด๋ ต์ง ์์ฃ ? ์ด์ ์ด ๋ ์์ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๋ ์์ธํ ์์๋ณผ๊น์? ๋ค์ ์น์ ์์ ๊ณ์๋ฉ๋๋ค! ๐โโ๏ธ๐จ
2. keyof์ ์ค์ ํ์ฉ ์ฌ๋ก ๐ ๏ธ
์, ์ด์ 'keyof'๋ฅผ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ์์๋ณผ ์ฐจ๋ก์์. ์ฌ๋ฌ๋ถ, ์ค๋น๋์ จ๋์? ๊ทธ๋ผ ์ถ๋ฐ~! ๐๐จ
2.1 ๊ฐ์ฒด์ ํค ํ์ ์ ํํ๊ธฐ
๋จผ์ , 'keyof'๋ฅผ ์ฌ์ฉํด ๊ฐ์ฒด์ ํค ํ์ ์ ์ ํํ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊ฒ์. ์ด๊ฑด ํนํ ํจ์๋ฅผ ์์ฑํ ๋ ์ ์ฉํ๋ต๋๋ค.
interface Config {
apiUrl: string;
timeout: number;
retryCount: number;
}
function updateConfig(config: Config, key: keyof Config, value: Config[keyof Config]) {
config[key] = value;
}
const myConfig: Config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retryCount: 3
};
updateConfig(myConfig, "timeout", 10000); // OK
updateConfig(myConfig, "apiUrl", "https://new-api.example.com"); // OK
updateConfig(myConfig, "foo", "bar"); // ์๋ฌ: "foo"๋ Config์ ํค๊ฐ ์๋
updateConfig(myConfig, "timeout", "5000"); // ์๋ฌ: number ํ์
์ string์ ํ ๋นํ ์ ์์
์ด ์์ ์์ 'updateConfig' ํจ์๋ 'Config' ๊ฐ์ฒด์ ํค๋ง ๋ฐ์ ์ ์๊ณ , ํด๋น ํค์ ๋ง๋ ํ์ ์ ๊ฐ๋ง ํ ๋นํ ์ ์์ด์. ์ด๋ ๊ฒ ํ๋ฉด ์ค์๋ก ์๋ชป๋ ํค๋ ๊ฐ์ ์ฌ์ฉํ๋ ๊ฑธ ๋ฐฉ์งํ ์ ์๋ต๋๋ค. ์์ ์ฑ up! ๐
2.2 ๋งคํ๋ ํ์ ๋ง๋ค๊ธฐ
'keyof'๋ ๋งคํ๋ ํ์ ์ ๋ง๋ค ๋๋ ์์ฃผ ์ ์ฉํด์. ๋งคํ๋ ํ์ ์ด ๋ญ๋๊ณ ์? ๊ธฐ์กด ํ์ ์ ๊ธฐ๋ฐ์ผ๋ก ์๋ก์ด ํ์ ์ ๋ง๋๋ ๊ฑฐ๋๋๋ค. ํ๋ฒ ๋ณผ๊น์?
interface Person {
name: string;
age: number;
email: string;
}
type Nullable<t> = { [K in keyof T]: T[K] | null };
const nullablePerson: Nullable<person> = {
name: "Alice",
age: null,
email: "alice@example.com"
};
</person></t>
์ฌ๊ธฐ์ 'Nullable' ํ์ ์ ์๋ ํ์ ์ ๋ชจ๋ ์์ฑ์ null๊ณผ ์ ๋์จ์ผ๋ก ๋ง๋ค์ด์ค์. ์ฆ, ๋ชจ๋ ์์ฑ์ด ์๋ ํ์ ์ด๊ฑฐ๋ null์ด ๋ ์ ์๋ ๊ฑฐ์ฃ . ์ด๋ฐ ์์ผ๋ก 'keyof'๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ์กด ํ์ ์ ๋ณํํด์ ์๋ก์ด ํ์ ์ ๋ง๋ค ์ ์์ด์. ์์ ๊ฟํ์ด์ฃ ? ๐ฏ
2.3 ํ์ ์์ ํ ๊ฐ์ฒด ์ํ
๊ฐ์ฒด๋ฅผ ์ํํ ๋๋ 'keyof'๋ฅผ ํ์ฉํ ์ ์์ด์. ํนํ 'for...in' ๋ฃจํ์ ํจ๊ป ์ฌ์ฉํ๋ฉด ํ์ ์์ ์ฑ์ ๋ณด์ฅํ ์ ์๋ต๋๋ค.
function logPerson(person: Person) {
for (const key in person) {
if (person.hasOwnProperty(key)) {
const k = key as keyof Person;
console.log(`${k}: ${person[k]}`);
}
}
}
const alice: Person = {
name: "Alice",
age: 30,
email: "alice@example.com"
};
logPerson(alice);
์ด ์์ ์์ 'key as keyof Person'์ TypeScript์๊ฒ 'key'๊ฐ 'Person' ํ์ ์ ํค๋ผ๋ ๊ฑธ ์๋ ค์ฃผ๋ ๊ฑฐ์์. ์ด๋ ๊ฒ ํ๋ฉด 'person[k]'์์ ํ์ ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๊ณ , ๊ฐ ์์ฑ์ ์์ ํ๊ฒ ์ ๊ทผํ ์ ์๋ต๋๋ค. ๐
๐ก Tip: 'keyof'๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ฒด์ ๊ตฌ์กฐ๋ฅผ ํ์ ์์คํ ์ ๋ฐ์ํ ์ ์์ด์. ์ด๋ ์ฝ๋์ ์์ ์ฑ์ ๋์ด๊ณ , ๋ฆฌํฉํ ๋ง์ ์ฝ๊ฒ ๋ง๋ค์ด์ค๋ต๋๋ค. ํนํ ํฐ ํ๋ก์ ํธ์์ ์ด๋ฐ ์ฅ์ ์ด ๋น์ ๋ฐํ๋ค๋ ๊ฑธ ๊ธฐ์ตํ์ธ์!
์ด๋์? 'keyof'์ ์ค์ ํ์ฉ ์ฌ๋ก๋ฅผ ๋ณด๋ ์ดํด๊ฐ ์ข ๋ ์ ๋์๋์? ์ด์ ์ฌ๋ฌ๋ถ๋ 'keyof'๋ฅผ ์ฌ์ฉํด์ ๋ ์์ ํ๊ณ ์ ์ฐํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ ๊ฑฐ์์. ๋ค์ ์น์ ์์๋ 'keyof'์ ๊ด๋ จ๋ ๊ณ ๊ธ ๊ธฐ๋ฒ๋ค์ ์ดํด๋ณผ ๊ฑฐ๋๊น ๊ธฐ๋ํด์ฃผ์ธ์! ๐
3. keyof์ ํจ๊ป ์ฌ์ฉํ๋ ๊ณ ๊ธ ๊ธฐ๋ฒ๋ค ๐
์, ์ด์ 'keyof'์ ๋ํด ๊ธฐ๋ณธ์ ์ธ ์ดํด๋ ํ์ จ์ ๊ฑฐ์์. ๊ทธ๋ผ ์ด์ ์ข ๋ ์ฌํ๋ ๋ด์ฉ์ผ๋ก ๋ค์ด๊ฐ๋ณผ๊น์? 'keyof'์ ํจ๊ป ์ฌ์ฉํ๋ฉด ๋์ฑ ๊ฐ๋ ฅํด์ง๋ TypeScript์ ๊ณ ๊ธ ๊ธฐ๋ฒ๋ค์ ์๊ฐํด๋๋ฆด๊ฒ์. ์ค๋น๋์ จ๋์? Let's go! ๐โโ๏ธ๐จ
3.1 ์กฐ๊ฑด๋ถ ํ์ ๊ณผ keyof
์กฐ๊ฑด๋ถ ํ์ ์ TypeScript์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์ธ๋ฐ์, 'keyof'์ ํจ๊ป ์ฌ์ฉํ๋ฉด ๋์ฑ ์ ์ฐํ ํ์ ์ ์๊ฐ ๊ฐ๋ฅํด์ ธ์.
type IfHasEmail<t> = "email" extends keyof T ? T["email"] : never;
interface Person {
name: string;
age: number;
email: string;
}
interface Company {
name: string;
address: string;
}
type PersonEmail = IfHasEmail<person>; // string
type CompanyEmail = IfHasEmail<company>; // never
</company></person></t>
์ด ์์ ์์ 'IfHasEmail' ํ์ ์ ์ฃผ์ด์ง ํ์ T์ 'email' ์์ฑ์ด ์๋์ง ํ์ธํ๊ณ , ์๋ค๋ฉด ๊ทธ ํ์ ์, ์๋ค๋ฉด 'never' ํ์ ์ ๋ฐํํด์. ์ด๋ ๊ฒ ํ๋ฉด ํน์ ์์ฑ์ ์กด์ฌ ์ฌ๋ถ์ ๋ฐ๋ผ ๋ค๋ฅธ ํ์ ์ ์ ์ํ ์ ์๋ต๋๋ค. ์์ ์ฉ์ฃ ? ๐
3.2 ์ ๋ค๋ฆญ ์ ์ฝ ์กฐ๊ฑด์ผ๋ก keyof ํ์ฉํ๊ธฐ
์ ๋ค๋ฆญ๊ณผ 'keyof'๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด ํ์ ์์ ์ฑ์ ๋์ด๋ฉด์๋ ์ ์ฐํ ํจ์๋ฅผ ๋ง๋ค ์ ์์ด์.
function pluck<t k extends keyof t>(obj: T, keys: K[]): T[K][] {
return keys.map(key => obj[key]);
}
const person = {
name: "Alice",
age: 30,
email: "alice@example.com",
favoriteColor: "blue"
};
const nameAndAge = pluck(person, ["name", "age"]); // ["Alice", 30]
const colorAndEmail = pluck(person, ["favoriteColor", "email"]); // ["blue", "alice@example.com"]
// const invalid = pluck(person, ["name", "invalid"]); // ์ปดํ์ผ ์๋ฌ!
</t>
์ด 'pluck' ํจ์๋ ๊ฐ์ฒด์์ ์ง์ ๋ ํค๋ค์ ๊ฐ๋ง ์ถ์ถํด์. 'K extends keyof T'๋ผ๋ ์ ์ฝ ์กฐ๊ฑด ๋๋ถ์, ์กด์ฌํ์ง ์๋ ํค๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ๋ต๋๋ค. ์์ ํ๊ณ ํธ๋ฆฌํ์ฃ ? ๐
3.3 Record ์ ํธ๋ฆฌํฐ ํ์ ๊ณผ keyof
'Record' ์ ํธ๋ฆฌํฐ ํ์ ์ 'keyof'์ ๊ถํฉ์ด ์ข์ ๋ ์์ด์์. ๊ฐ์ฒด์ ํค-๊ฐ ์์ ํ์ ์ผ๋ก ์ ์ํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ต๋๋ค.
type Fruit = "apple" | "banana" | "orange";
type FruitInfo = { color: string; taste: string };
type FruitCatalog = Record<fruit fruitinfo>;
const fruitCatalog: FruitCatalog = {
apple: { color: "red", taste: "sweet" },
banana: { color: "yellow", taste: "sweet" },
orange: { color: "orange", taste: "citrusy" }
};
type FruitColors = Record<keyof fruitcatalog string>;
const fruitColors: FruitColors = {
apple: "red",
banana: "yellow",
orange: "orange"
};
</keyof></fruit>
์ฌ๊ธฐ์ 'FruitCatalog'๋ ๊ฐ ๊ณผ์ผ์ ๋ํ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๊ณ , 'FruitColors'๋ 'FruitCatalog'์ ํค๋ฅผ ๊ทธ๋๋ก ๊ฐ์ ธ์์ ์๋ก์ด ํ์ ์ ๋ง๋ค์ด๋์ด์. ์ด๋ ๊ฒ ํ๋ฉด ๊ธฐ์กด ํ์ ์ ๊ตฌ์กฐ๋ฅผ ์ฌ์ฌ์ฉํ๋ฉด์ ์๋ก์ด ํ์ ์ ์ ์ํ ์ ์๋ต๋๋ค. ์์ ๊ฟํ์ด์ฃ ? ๐ฏ
๐ Deep Dive: 'keyof'์ 'Record'๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด, ๊ฐ์ฒด์ ๊ตฌ์กฐ๋ฅผ ๋์ ์ผ๋ก ์ ์ํ๋ฉด์๋ ํ์ ์์ ์ฑ์ ์ ์งํ ์ ์์ด์. ์ด๋ ํนํ API ์๋ต ์ฒ๋ฆฌ๋ ์ค์ ๊ฐ์ฒด ๊ด๋ฆฌ ๋ฑ์์ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋ ์ ์๋ต๋๋ค!
์ด๋ ์ธ์? 'keyof'์ ํจ๊ป ์ฌ์ฉํ๋ ๊ณ ๊ธ ๊ธฐ๋ฒ๋ค์ ๋ณด๋ TypeScript์ ์ง๊ฐ๊ฐ ๋ ์ ๋ณด์ด์๋์? ์ด๋ฐ ๊ธฐ๋ฒ๋ค์ ์ ํ์ฉํ๋ฉด ๋์ฑ ์์ ํ๊ณ ์ ์ฐํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ต๋๋ค. ๋ค์ ์น์ ์์๋ 'keyof'๋ฅผ ์ค์ ํ๋ก์ ํธ์์ ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๊ตฌ์ฒด์ ์ธ ์์ ๋ฅผ ํตํด ์์๋ณผ ๊ฑฐ์์. ๊ธฐ๋๋์ง ์๋์? ๐
4. keyof๋ฅผ ํ์ฉํ ์ค์ ํ๋ก์ ํธ ์์ ๐ผ
์, ์ด์ 'keyof'๋ฅผ ์ค์ ํ๋ก์ ํธ์์ ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๊ตฌ์ฒด์ ์ธ ์์ ๋ฅผ ํตํด ์์๋ณผ ๊ฑฐ์์. ์ค์ ์์ ์ด๋ป๊ฒ ์ฐ์ด๋์ง ๋ณด๋ฉด ๋ ์ ์ดํด๋ ๊ฑฐ์์. ์ค๋น๋์ จ๋์? ๊ทธ๋ผ ๊ณ ๊ณ ์ฝ~! ๐
4.1 API ์๋ต ์ฒ๋ฆฌํ๊ธฐ
์น ๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด API ์๋ต์ ์ฒ๋ฆฌํด์ผ ํ ์ผ์ด ๋ง์ฃ ? ์ด๋ 'keyof'๋ฅผ ํ์ฉํ๋ฉด ํ์ ์์ ์ฑ์ ๋์ด๋ฉด์๋ ์ ์ฐํ๊ฒ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ด์.
interface ApiResponse {
userId: number;
id: number;
title: string;
completed: boolean;
}
function processApiResponse<k extends keyof apiresponse>(
response: ApiResponse,
key: K,
processor: (value: ApiResponse[K]) => void
) {
processor(response[key]);
}
const response: ApiResponse = {
userId: 1,
id: 1,
title: "delectus aut autem",
completed: false
};
processApiResponse(response, "title", (title) => {
console.log(title.toUpperCase());
});
processApiResponse(response, "completed", (completed) => {
console.log(completed ? "Task done!" : "Task pending...");
});
// ์ปดํ์ผ ์๋ฌ ๋ฐ์!
// processApiResponse(response, "invalid", (value) => {
// console.log(value);
// });
</k>
์ด ์์ ์์ 'processApiResponse' ํจ์๋ API ์๋ต์ ํน์ ํ๋๋ฅผ ์ฒ๋ฆฌํ๋ ํจ์์์. 'K extends keyof ApiResponse'๋ผ๋ ์ ์ฝ ์กฐ๊ฑด ๋๋ถ์, ApiResponse์ ํค๋ง ์ฌ์ฉํ ์ ์๊ณ , ๊ฐ ํค์ ๋ง๋ ์ฌ๋ฐ๋ฅธ ํ์ ์ ๊ฐ๋ง ์ฒ๋ฆฌํ ์ ์๋ต๋๋ค. ์์ ํ๊ณ ํธ๋ฆฌํ์ฃ ? ๐
4.2 ํผ ์ ํจ์ฑ ๊ฒ์ฌ
์น ํผ์ ๋ค๋ฃฐ ๋ 'keyof'๋ฅผ ํ์ฉํ๋ฉด ํ์ ์์ ํ ์ ํจ์ฑ ๊ฒ์ฌ ๋ก์ง์ ๊ตฌํํ ์ ์์ด์.
interface FormValues {
username: string;
email: string;
age: number;
}
type FormErrors = Partial<record formvalues string>>;
function validate(values: FormValues): FormErrors {
const errors: FormErrors = {};
if (values.username.length < 3) {
errors.username = "Username must be at least 3 characters long";
}
if (!values.email.includes("@")) {
errors.email = "Invalid email address";
}
if (values.age < 18) {
errors.age = "You must be at least 18 years old";
}
return errors;
}
const formValues: FormValues = {
username: "Al",
email: "alice@example",
age: 16
};
const errors = validate(formValues);
console.log(errors);
// {
// username: "Username must be at least 3 characters long",
// email: "Invalid email address",
// age: "You must be at least 18 years old"
// }
</record>
์ด ์์ ์์ 'FormErrors' ํ์ ์ 'FormValues'์ ๋ชจ๋ ํค๋ฅผ ์ต์ ๋ํ ๋ฌธ์์ด ๊ฐ์ผ๋ก ๋ง๋ค์ด์. ์ด๋ ๊ฒ ํ๋ฉด ๊ฐ ํ๋์ ๋ํ ์๋ฌ ๋ฉ์์ง๋ฅผ ํ์ ์์ ํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ต๋๋ค. ํผ ์ฒ๋ฆฌ๊ฐ ํ๊ฒฐ ํธํด์ง๊ฒ ์ฃ ? ๐
4.3 ์ค์ ๊ฐ์ฒด ๊ด๋ฆฌ
์ ํ๋ฆฌ์ผ์ด์ ์ ์ค์ ์ ๊ด๋ฆฌํ ๋๋ 'keyof'๋ฅผ ํ์ฉํ๋ฉด ์ข์์. ํนํ ์ค์ ๊ฐ์ ๋์ ์ผ๋ก ์ ๋ฐ์ดํธํด์ผ ํ ๋ ์ ์ฉํ๋ต๋๋ค.
interface AppConfig {
apiUrl: string;
theme: "light" | "dark";
language: "en" | "ko" | "ja";
notifications: boolean;
}
class ConfigManager {
private config: AppConfig;
constructor(initialConfig: AppConfig) {
this.config = initialConfig;
}
get<k extends keyof appconfig>(key: K): AppConfig[K] {
return this.config[key];
}
set<k extends keyof appconfig>(key: K, value: AppConfig[K]): void {
this.config[key] = value;
}
getAll(): AppConfig {
return this.config;
}
}
const configManager = new ConfigManager({
apiUrl: "https://api.example.com",
theme: "light",
language: "en",
notifications: true
});
console.log(configManager.get("theme")); // "light"
configManager.set("theme", "dark");
console.log(configManager.get("theme")); // "dark"
// ์ปดํ์ผ ์๋ฌ ๋ฐ์!
// configManager.set("theme", "blue");
// configManager.set("invalidKey", "value");
</k></k>
์ด 'ConfigManager' ํด๋์ค๋ 'keyof'๋ฅผ ํ์ฉํด ํ์ ์์ ํ ์ค์ ๊ด๋ฆฌ๋ฅผ ๊ตฌํํ์ด์. 'get' ๋ฉ์๋์ 'set' ๋ฉ์๋๋ 'AppConfig'์ ํค๋ง ๋ฐ์ ์ ์๊ณ , ๊ฐ ํค์ ๋ง๋ ์ฌ๋ฐ๋ฅธ ํ์ ์ ๊ฐ๋ง ์ค์ ํ ์ ์๋ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ค์ ๊ด๋ฆฌ๋ฅผ ๋์ฑ ์์ ํ๊ณ ํจ์จ์ ์ผ๋ก ํ ์ ์์ด์. ๐จโ๐ป
๐ก Pro Tip: 'keyof'๋ฅผ ํ์ฉํ ์ด๋ฐ ํจํด๋ค์ ํนํ ํฐ ๊ท๋ชจ์ ํ๋ก์ ํธ์์ ๋น์ ๋ฐํด์. ์ฝ๋์ ์์ ์ฑ์ ๋์ด๊ณ , ์ค์๋ฅผ ์ค์ด๋ฉฐ, ๋ฆฌํฉํ ๋ง์ ์ฝ๊ฒ ๋ง๋ค์ด์ค๋ต๋๋ค. ์ฌ๋ฅ๋ท ๊ฐ์ ๋ณต์กํ ํ๋ซํผ์ ๊ฐ๋ฐํ ๋ ์ด๋ฐ ๊ธฐ๋ฒ๋ค์ ํ์ฉํ๋ฉด ์ ๋ง ํฐ ๋์์ด ๋ ๊ฑฐ์์!
์ด๋ ์ธ์? ์ค์ ํ๋ก์ ํธ์์ 'keyof'๋ฅผ ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ๊ฐ์ด ์ข ์ค์๋์? ์ด๋ฐ ๊ธฐ๋ฒ๋ค์ ์ ํ์ฉํ๋ฉด ๋์ฑ ์์ ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ต๋๋ค. ๋ค์ ์น์ ์์๋ 'keyof'๋ฅผ ์ฌ์ฉํ ๋ ์ฃผ์ํด์ผ ํ ์ ๋ค์ ๋ํด ์์๋ณผ ๊ฑฐ์์. ๊ธฐ๋๋์ง ์๋์? ๐
5. keyof ์ฌ์ฉ ์ ์ฃผ์์ฌํญ โ ๏ธ
์, ์ด์ 'keyof'์ ๊ฐ๋ ฅํจ์ ๋ํด ์ถฉ๋ถํ ์์๋ณด์ จ์ฃ ? ํ์ง๋ง ๋ชจ๋ ๋๊ตฌ๊ฐ ๊ทธ๋ ๋ฏ, 'keyof'๋ ์ฃผ์ํด์ ์ฌ์ฉํด์ผ ํด์. ์ด๋ฒ ์น์ ์์๋ 'keyof'๋ฅผ ์ฌ์ฉํ ๋ ์ฃผ์ํด์ผ ํ ์ ๋ค์ ๋ํด ์์๋ณผ ๊ฑฐ์์. ์ค๋น๋์ จ๋์? ๊ทธ๋ผ ๊ณ ๊ณ ! ๐
5.1 ์ธ๋ฑ์ค ์๊ทธ๋์ฒ์ keyof
์ธ๋ฑ์ค ์๊ทธ๋์ฒ๋ฅผ ์ฌ์ฉํ ํ์ ์ 'keyof'๋ฅผ ์ ์ฉํ ๋๋ ์ฃผ์๊ฐ ํ์ํด์. ์์์น ๋ชปํ ๊ฒฐ๊ณผ๊ฐ ๋์ฌ ์ ์๊ฑฐ๋ ์.
interface StringMap {
[key: string]: string;
}
type StringMapKeys = keyof StringMap; // string | number
์ด๋ผ? 'StringMapKeys'๊ฐ 'string'๋ง ๋ ๊ฑฐ๋ผ๊ณ ์๊ฐํ์ จ๋์? ์ค์ ๋ก๋ 'string | number'๊ฐ ๋ผ์. ์ ๊ทธ๋ด๊น์? JavaScript์์ ๊ฐ์ฒด์ ํค๋ ๋ด๋ถ์ ์ผ๋ก ๋ฌธ์์ด๋ก ๋ณํ๋๊ธฐ ๋๋ฌธ์ด์์. ๊ทธ๋์ ์ซ์ ํค๋ ์ ํจํ ํค๋ก ์ทจ๊ธ๋๋ ๊ฑฐ์ฃ .
์ด๋ฐ ํน์ฑ ๋๋ฌธ์ ์์์น ๋ชปํ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์ ์์ด์. ํนํ strictํ ํ์ ์ฒดํฌ๋ฅผ ํ๋ ๊ฒฝ์ฐ์ ์ฃผ์๊ฐ ํ์ํฉ๋๋ค.
5.2 ์ ๋์จ ํ์ ๊ณผ keyof
์ ๋์จ ํ์ ์ 'keyof'๋ฅผ ์ฌ์ฉํ ๋๋ ์ฃผ์๊ฐ ํ์ํด์. ๊ฒฐ๊ณผ๊ฐ ์ง๊ด์ ์ด์ง ์์ ์ ์๊ฑฐ๋ ์.
interface Dog {
name: string;
breed: string;
}
interface Cat {
name: string;
lives: number;
}
type Pet = Dog | Cat;
type PetKeys = keyof Pet; // "name"
'PetKeys'๊ฐ "name" | "breed" | "lives"๊ฐ ๋ ๊ฑฐ๋ผ๊ณ ์๊ฐํ์ จ๋์? ์ค์ ๋ก๋ "name"๋ง ๋ฉ๋๋ค. ์๋ํ๋ฉด 'keyof'๋ ์ ๋์จ ํ์ ์ ๋ชจ๋ ๋ฉค๋ฒ์ ๊ณตํต์ผ๋ก ์กด์ฌํ๋ ํค๋ง ์ถ์ถํ๊ธฐ ๋๋ฌธ์ด์์. ์ด๋ฐ ํน์ฑ์ ์ ์ดํด ํ๊ณ ์์ด์ผ ์์์น ๋ชปํ ํ์ ์ค๋ฅ๋ฅผ ๋ฐฉ์งํ ์ ์๋ต๋๋ค.
5.3 ํ์ ์ถ๋ก ๊ณผ keyof
๋๋ก๋ TypeScript์ ํ์ ์ถ๋ก ์ด 'keyof'์ ํจ๊ป ์ฌ์ฉ๋ ๋ ์์๊ณผ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ผ ์ ์์ด์. ํนํ ์ ๋ค๋ฆญ๊ณผ ํจ๊ป ์ฌ์ฉํ ๋ ์ฃผ์๊ฐ ํ์ํฉ๋๋ค.
function getProperty<t k extends keyof t>(obj: T, key: K) {
return obj[key];
}
const person = { name: "Alice", age: 30 };
const name = getProperty(person, "name"); // OK
const age = getProperty(person, "age"); // OK
// ๋ค์์ ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ฐ์์ํต๋๋ค!
// const invalid = getProperty(person, "invalid");
</t>
์ด ์์ ์์ 'person' ๊ฐ์ฒด์ ํ์ ์ ๋ช ์์ ์ผ๋ก ์ ์ธ๋์ง ์์์ง๋ง, TypeScript๋ ๊ฐ์ฒด ๋ฆฌํฐ๋ด๋ก๋ถํฐ ํ์ ์ ์ถ๋ก ํด์. ๊ทธ๋์ 'getProperty' ํจ์๋ 'person' ๊ฐ์ฒด์ ์ค์ ํค๋ง ํ์ฉํ๊ฒ ๋ฉ๋๋ค. ์ด๋ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ํ๋ ๋์์ด์ง๋ง, ๋๋ก๋ ์์์น ๋ชปํ ์ ์ฝ์ ๊ฐํ ์ ์์ด์.
5.4 readonly ์์ฑ๊ณผ keyof
'readonly' ์์ฑ์ ๊ฐ์ง ํ์ ์ 'keyof'๋ฅผ ์ฌ์ฉํ ๋๋ ์ฃผ์๊ฐ ํ์ํด์. 'readonly' ์์ฑ์ 'keyof'์ ๊ฒฐ๊ณผ์ ์ํฅ์ ๋ฏธ์น์ง ์๊ธฐ ๋๋ฌธ์ด์ฃ .
interface ReadonlyPerson {
readonly name: string;
readonly age: number;
}
type PersonKeys = keyof ReadonlyPerson; // "name" | "age"
const person: ReadonlyPerson = { name: "Alice", age: 30 };
let key: PersonKeys = "name";
console.log(person[key]); // OK
person[key] = "Bob"; // ์ปดํ์ผ ์๋ฌ: ์ฝ๊ธฐ ์ ์ฉ ์์ฑ์ด๋ฏ๋ก 'name'์ ํ ๋นํ ์ ์์ต๋๋ค.
'keyof'๋ 'readonly' ์์ฑ์ ๋ฌด์ํ๊ณ ๋ชจ๋ ํค๋ฅผ ์ถ์ถํด์. ํ์ง๋ง ์ค์ ๋ก ๊ทธ ์์ฑ์ ๊ฐ์ ํ ๋นํ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค. ์ด๋ฐ ํน์ฑ์ ์ ์ดํดํ๊ณ ์์ด์ผ ์ฝ๋์ ์๋๋ฅผ ์ ํํ ํํํ ์ ์์ด์.
โ ๏ธ Warning: 'keyof'๋ฅผ ์ฌ์ฉํ ๋๋ ํญ์ ๊ฒฐ๊ณผ ํ์ ์ ์ ํ์ธํด์ผ ํด์. ํนํ ๋ณต์กํ ํ์ ์ด๋ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ ๋๋ ๋์ฑ ๊ทธ๋ ์ฃ . TypeScript์ ํ์ ์ถ๋ก ์ ๊ณผ์ ํ์ง ๋ง๊ณ , ํ์ํ๋ค๋ฉด ๋ช ์์ ์ผ๋ก ํ์ ์ ์ ์ธํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์์.
์ด๋ ์ธ์? 'keyof'๋ฅผ ์ฌ์ฉํ ๋ ์ฃผ์ํด์ผ ํ ์ ๋ค์ ๋ํด ์์๋ดค์ด์. ์ด๋ฐ ์ฃผ์์ฌํญ๋ค์ ์ ๊ธฐ์ตํ๊ณ ์์ผ๋ฉด, 'keyof'๋ฅผ ๋์ฑ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ ์ ์์ ๊ฑฐ์์. ํ์ ์์ ์ฑ์ ๋์ด๋ฉด์๋ ์ ์ฐํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐ ํฐ ๋์์ด ๋ ๊ฑฐ๋๋๋ค. ๋ค์ ์น์ ์์๋ 'keyof'์ ๊ด๋ จ๋ ์์ฃผ ๋ฌป๋ ์ง๋ฌธ๋ค์ ์ดํด๋ณผ ๊ฑฐ์์. ๊ธฐ๋๋์ง ์๋์? ๐
6. keyof ๊ด๋ จ FAQ ๐ค
์, ์ด์ 'keyof'์ ๋ํด ๊ฝค ๋ง์ด ์์๋ณด์ จ์ฃ ? ํ์ง๋ง ์์ง ๊ถ๊ธํ ์ ๋ค์ด ์์ ๊ฑฐ์์. ์ด๋ฒ ์น์ ์์๋ 'keyof'์ ๊ด๋ จํด ์์ฃผ ๋ฌป๋ ์ง๋ฌธ๋ค์ ๋ชจ์๋ดค์ด์. ํจ๊ป ์ดํด๋ณผ๊น์? Let's go! ๐
Q1: keyof์ typeof์ ์ฐจ์ด์ ์ ๋ฌด์์ธ๊ฐ์?
A: 'keyof'์ 'typeof'๋ ๋น์ทํด ๋ณด์ด์ง๋ง ์์ ํ ๋ค๋ฅธ ์ฐ์ฐ์์์.
- 'keyof'๋ ํ์ ๋๋ ์ธํฐํ์ด์ค์ ๋ชจ๋ ํค๋ฅผ ์ ๋์จ ํ์ ์ผ๋ก ์ถ์ถํด์.
- 'typeof'๋ ๊ฐ์ ํ์ ์ ์ถ๋ก ํด์.
์๋ฅผ ๋ค์ด๋ณผ๊น์?
const person = { name: "Alice", age: 30 };
type PersonKeys = keyof typeof person; // "name" | "age"
type PersonType = typeof person; // { name: string; age: number; }
์ฌ๊ธฐ์ 'keyof typeof'๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด ๊ฐ์ผ๋ก๋ถํฐ ํค ํ์ ์ ์ถ์ถํ ์ ์์ด์. ๊ฝค ์ ์ฉํ ์กฐํฉ์ด์ฃ ? ๐
Q2: keyof๋ก ์ถ์ถํ ํค๋ฅผ ๋ค์ ๊ฐ์ผ๋ก ์ฌ์ฉํ ์ ์๋์?
A: ๋ค, ๊ฐ๋ฅํด์! ํ์ง๋ง ์ฝ๊ฐ์ ํธ๋ฆญ์ด ํ์ํฉ๋๋ค.
interface Person {
name: string;
age: number;
}
const personKey: keyof Person = "name";
const key: "name" | "age" = personKey;
function getPersonValue(key: keyof Person) {
const person: Person = { name: "Alice", age: 30 };
return person[key];
}
const value = getPersonValue("name"); // OK
์ด๋ ๊ฒ ํ๋ฉด 'keyof'๋ก ์ถ์ถํ ํค๋ฅผ ๊ฐ์ผ๋ก ์ฌ์ฉํ ์ ์์ด์. ํ์ง๋ง ์ด ๋ฐฉ๋ฒ์ ํ์ ์์ ์ฑ์ ์๋ฒฝํ๊ฒ ๋ณด์ฅํ์ง๋ ์์ผ๋ ์ฃผ์ํด์ ์ฌ์ฉํด์ผ ํด์.
Q3: keyof๋ฅผ ์ฌ์ฉํ ๋ ์ฑ๋ฅ ๋ฌธ์ ๋ ์๋์?
A: ๊ฑฑ์ ๋ง์ธ์! 'keyof'๋ ์ปดํ์ผ ํ์์๋ง ์๋ํ๋ TypeScript์ ํ์ ์ฐ์ฐ์์์. ๋ฐ๋ผ์ ๋ฐํ์ ์ฑ๋ฅ์๋ ์ ํ ์ํฅ์ ๋ฏธ์น์ง ์์์. ์คํ๋ ค ํ์ ์์ ์ฑ์ ๋์ฌ ๋ฐํ์ ์๋ฌ๋ฅผ ์ค์ด๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
Q4: keyof๋ฅผ ๋ฐฐ์ด์ ์ฌ์ฉํ ์ ์๋์?
A: ๋ค, ์ฌ์ฉํ ์ ์์ด์! ํ์ง๋ง ๊ฒฐ๊ณผ๊ฐ ์์๊ณผ ๋ค๋ฅผ ์ ์์ผ๋ ์ฃผ์ํด์ผ ํด์.
type ArrayKeys = keyof string[]; // number | "length" | "push" | "pop" | "concat" | ...
๋ฐฐ์ด์ 'keyof'๋ฅผ ์ฌ์ฉํ๋ฉด ์ธ๋ฑ์ค(number)์ ๋ฐฐ์ด์ ๋ชจ๋ ๋ฉ์๋ ์ด๋ฆ์ด ์ ๋์จ ํ์ ์ผ๋ก ์ถ์ถ๋ผ์. ์ด๋ ๋ฐฐ์ด์ด ์ฌ์ค ๊ฐ์ฒด์ด๊ธฐ ๋๋ฌธ์ด์์.
Q5: keyof์ mapped types๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ์ข์ ๋ฐฉ๋ฒ์ด ์๋์?
A: ๋ค, 'keyof'์ mapped types๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด ์์ฃผ ๊ฐ๋ ฅํ ํ์ ๋ณํ์ ํ ์ ์์ด์. ์๋ฅผ ๋ค์ด๋ณผ๊น์?
type Optional<t> = {
[K in keyof T]?: T[K];
};
interface Person {
name: string;
age: number;
}
type OptionalPerson = Optional<person>;
// ๊ฒฐ๊ณผ: { name?: string; age?: number; }
</person></t>
์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋ ์์ฑ์ ์ ํ์ ์ผ๋ก ๋ง๋๋ ํ์ ์ ์ฝ๊ฒ ๋ง๋ค ์ ์์ด์. ์ด๋ฐ ํจํด์ ์ค์ ํ๋ก์ ํธ์์ ์ ๋ง ์ ์ฉํ๊ฒ ์ฌ์ฉ๋๋ต๋๋ค!
๐ก Pro Tip: 'keyof'์ mapped types๋ฅผ ์กฐํฉํ๋ฉด ๊ธฐ์กด ํ์ ์ ๋ณํํ๋ ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ๋ง๋ค ์ ์์ด์. ์ด๋ฅผ ํ์ฉํ๋ฉด ์ฝ๋ ์ค๋ณต์ ์ค์ด๊ณ ํ์ ์์ ์ฑ์ ๋์ผ ์ ์๋ต๋๋ค. ํนํ ํฐ ํ๋ก์ ํธ์์ ์ด๋ฐ ๊ธฐ๋ฒ๋ค์ด ๋น์ ๋ฐํ๋ค๋ ๊ฑธ ๊ธฐ์ตํ์ธ์!
์ด๋ ์ธ์? 'keyof'์ ๋ํด ๊ถ๊ธํ๋ ์ ๋ค์ด ์ข ํด์๋์ จ๋์? ์ด๋ฐ ์ง๋ฌธ๋ค์ ํตํด 'keyof'์ ๋ค์ํ ์ธก๋ฉด์ ์ดํดํ ์ ์์๊ธธ ๋ฐ๋ผ์. 'keyof'๋ ์ ๋ง ๊ฐ๋ ฅํ ๋๊ตฌ์ง๋ง, ๊ทธ๋งํผ ์ธ์ฌํ๊ฒ ๋ค๋ค์ผ ํ๋ ๋ ์์ด์์. ์ ์ดํดํ๊ณ ์ฌ์ฉํ๋ค๋ฉด, ์ฌ๋ฌ๋ถ์ TypeScript ์ฝ๋๋ฅผ ํ์ธต ๋ ๊ฒฌ๊ณ ํ๊ฒ ๋ง๋ค์ด์ค ๊ฑฐ์์. ๋ค์ ์น์ ์์๋ 'keyof'๋ฅผ ํ์ฉํ ์ค์ ์์ ๋ฅผ ๋ ์์ธํ ์ดํด๋ณผ ๊ฑฐ์์. ๊ธฐ๋๋์ง ์๋์? ๐
7. keyof๋ฅผ ํ์ฉํ ์ค์ ์์ ๐ป
์, ์ด์ 'keyof'์ ๋ํด ๊ฝค ๋ง์ด ์๊ฒ ๋์ จ์ฃ ? ์ด๋ก ๋ ์ค์ํ์ง๋ง, ์ค์ ๋ก ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง ๋ณด๋ ๊ฒ๋ ์ค์ํด์. ์ด๋ฒ ์น์ ์์๋ 'keyof'๋ฅผ ํ์ฉํ ์ค์ ์์ ๋ฅผ ์์ธํ ์ดํด๋ณผ ๊ฑฐ์์. ์ค๋น๋์ จ๋์? ๊ทธ๋ผ ์์ํด๋ณผ๊น์? ๐
์์ 1: ํ์ ์์ ํ ๊ฐ์ฒด ๋ณต์ฌ ํจ์
๊ฐ์ฒด๋ฅผ ๋ณต์ฌํ ๋ ํน์ ์์ฑ๋ง ์ ํ์ ์ผ๋ก ๋ณต์ฌํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น์? 'keyof'๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ ์์ ํ๊ฒ ์ด๋ฅผ ๊ตฌํํ ์ ์์ด์.
function partialCopy<t k extends keyof t>(obj: T, keys: K[]): Pick<t k> {
const result = {} as Pick<t k>;
keys.forEach(key => {
result[key] = obj[key];
});
return result;
}
interface Person {
name: string;
age: number;
address: string;
phone: string;
}
const person: Person = {
name: "Alice",
age: 30,
address: "123 Main St",
phone: "555-1234"
};
const partialPerson = partialCopy(person, ["name", "age"]);
console.log(partialPerson); // { name: "Alice", age: 30 }
// ์ปดํ์ผ ์๋ฌ ๋ฐ์!
// const invalidCopy = partialCopy(person, ["name", "invalid"]);
</t></t></t>
์ด ์์ ์์ 'partialCopy' ํจ์๋ ์๋ณธ ๊ฐ์ฒด์ ๋ณต์ฌํ ํค ๋ฐฐ์ด์ ๋ฐ์ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ฐํํด์. 'K extends keyof T'๋ผ๋ ์ ์ฝ ์กฐ๊ฑด ๋๋ถ์ ์กด์ฌํ์ง ์๋ ํค๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํด์. ์์ ํ๊ณ ์ ์ฐํ์ฃ ? ๐
์์ 2: ํ์ ์์ ํ ์ด๋ฒคํธ ์์คํ
์ด๋ฒคํธ ๊ธฐ๋ฐ ์์คํ ์ ๊ตฌํํ ๋ 'keyof'๋ฅผ ํ์ฉํ๋ฉด ํ์ ์์ ์ฑ์ ํฌ๊ฒ ๋์ผ ์ ์์ด์. ํ๋ฒ ๋ณผ๊น์?
type EventMap = {
click: { x: number; y: number };
change: { oldValue: string; newValue: string };
submit: { data: object };
}
class EventEmitter {
private listeners: Partial<{ [K in keyof EventMap]: ((data: EventMap[K]) => void)[] }> = {};
on<k extends keyof eventmap>(event: K, callback: (data: EventMap[K]) => void) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(callback);
}
emit<k extends keyof eventmap>(event: K, data: EventMap[K]) {
if (this.listeners[event]) {
this.listeners[event]!.forEach(callback => callback(data));
}
}
}
const emitter = new EventEmitter();
emitter.on("click", ({ x, y }) => {
console.log(`Clicked at (${x}, ${y})`);
});
emitter.on("change", ({ oldValue, newValue }) => {
console.log(`Value changed from ${oldValue} to ${newValue}`);
});
emitter.emit("click", { x: 10, y: 20 });
emitter.emit("change", { oldValue: "old", newValue: "new" });
// ์ปดํ์ผ ์๋ฌ ๋ฐ์!
// emitter.on("invalid", () => {});
// emitter.emit("click", { invalid: "data" });
</k></k>
์ด ์์ ์์ 'EventEmitter' ํด๋์ค๋ 'keyof'๋ฅผ ์ฌ์ฉํด ํ์ ์์ ํ ์ด๋ฒคํธ ์์คํ ์ ๊ตฌํํ์ด์. ๊ฐ ์ด๋ฒคํธ ํ์ ์ ๋ง๋ ๋ฐ์ดํฐ๋ง ์ ๋ฌํ ์ ์๊ณ , ์กด์ฌํ์ง ์๋ ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํด์. ๋ฉ์ง์ฃ ? ๐
์์ 3: ํ์ ์์ ํ ์ํ ๊ด๋ฆฌ
์ํ ๊ด๋ฆฌ ์์คํ ์ ๊ตฌํํ ๋๋ 'keyof'๋ฅผ ํ์ฉํ๋ฉด ์ข์์. Redux ์คํ์ผ์ ๊ฐ๋จํ ์ํ ๊ด๋ฆฌ ์์คํ ์ ๋ง๋ค์ด๋ณผ๊น์?
interface State {
user: { name: string; age: number } | null;
posts: string[];
isLoading: boolean;
}
type Action =
| { type: "SET_USER"; payload: State["user"] }
| { type: "ADD_POST"; payload: string }
| { type: "SET_LOADING"; payload: boolean };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "SET_USER":
return { ...state, user: action.payload };
case "ADD_POST":
return { ...state, posts: [...state.posts, action.payload] };
case "SET_LOADING":
return { ...state, isLoading: action.payload };
default:
return state;
}
}
function createStore<s>(initialState: S, reducer: (state: S, action: any) => S) {
let state = initialState;
return {
getState: () => state,
dispatch: (action: any) => {
state = reducer(state, action);
},
select: <k extends keyof s>(selector: K): S[K] => state[selector]
};
}
const store = createStore<state>(
{ user: null, posts: [], isLoading: false },
reducer
);
store.dispatch({ type: "SET_USER", payload: { name: "Alice", age: 30 } });
store.dispatch({ type: "ADD_POST", payload: "Hello, world!" });
console.log(store.select("user")); // { name: "Alice", age: 30 }
console.log(store.select("posts")); // ["Hello, world!"]
// ์ปดํ์ผ ์๋ฌ ๋ฐ์!
// console.log(store.select("invalid"));
</state></k></s>
์ด ์์ ์์ 'createStore' ํจ์๋ 'keyof'๋ฅผ ์ฌ์ฉํด ํ์ ์์ ํ 'select' ๋ฉ์๋๋ฅผ ๊ตฌํํ์ด์. ์ด ๋ฉ์๋๋ ์ํ์ ํน์ ๋ถ๋ถ๋ง์ ์์ ํ๊ฒ ์ ํํ ์ ์๊ฒ ํด์ค์. ์กด์ฌํ์ง ์๋ ์ํ ํค๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ์ฃ . ์ํ ๊ด๋ฆฌ๊ฐ ํ๊ฒฐ ์์ ํด์ก์ด์! ๐
๐ Advanced Tip: ์ด๋ฐ ํจํด๋ค์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ ๋๋ ์ํฉ์ ๋ง๊ฒ ์กฐ๊ธ์ฉ ์์ ์ด ํ์ํ ์ ์์ด์. ์๋ฅผ ๋ค์ด, ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ํ๋ฅผ ๋ ์ธ๋ถํํ๊ฑฐ๋, ๋น๋๊ธฐ ์์ ์ ์ํ ๋ฏธ๋ค์จ์ด๋ฅผ ์ถ๊ฐํ๋ ๋ฑ์ ์์ ์ด ํ์ํ ์ ์์ฃ . ํ์ง๋ง ๊ธฐ๋ณธ ์์ด๋์ด๋ ๊ฐ์์ - 'keyof'๋ฅผ ํ์ฉํด ํ์ ์์ ์ฑ์ ๋์ด๋ ๊ฑฐ์ฃ !
์ด๋ ์ธ์? ์ด๋ฐ ์ค์ ์์ ๋ค์ ๋ณด๋ 'keyof'์ ํ์ฉ ๊ฐ๋ฅ์ฑ์ด ๋ ์๋ฟ์ง ์๋์? 'keyof'๋ ๋จ์ํ ๊ฐ์ฒด์ ํค๋ฅผ ๋ค๋ฃจ๋ ๊ฒ์ ๋์ด์, ํ์ ์์คํ ์ ๋ฐ์ ๊ฑธ์ณ ์์ ์ฑ๊ณผ ์ ์ฐ์ฑ์ ๋์ด๋ ๊ฐ๋ ฅํ ๋๊ตฌ๋๋๋ค. ์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ์์๋ ์ด๋ฐ ํจํด๋ค์ ์ ์ฉํด๋ณด์ธ์. ์ฝ๋์ ํ์ง์ด ํ์ธต ๋ ๋์์ง ๊ฑฐ์์! ๐
8. ๋ง๋ฌด๋ฆฌ: keyof์ ๋ฏธ๋์ ๋ฐ์ ๋ฐฉํฅ ๐ฎ
์, ์ด์ 'keyof'์ ๋ํด ์ ๋ง ๋ง์ด ์์๋ณด์ จ์ด์. ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ์ค์ ์์ ๊น์ง, 'keyof'์ ๋ค์ํ ๋ฉด๋ชจ๋ฅผ ์ดํด๋ดค์ฃ . ๊ทธ๋ ๋ค๋ฉด ์ด์ 'keyof'์ ๋ฏธ๋๋ ์ด๋จ๊น์? TypeScript์ ํจ๊ป ์ด๋ป๊ฒ ๋ฐ์ ํด ๋๊ฐ์ง ํ๋ฒ ์์ธกํด๋ณผ๊น์? ๐ค
1. ๋ ๊ฐ๋ ฅํ ํ์ ์ถ๋ก
TypeScript ํ์ ๊ณ์ํด์ ํ์ ์ถ๋ก ๋ฅ๋ ฅ์ ๊ฐ์ ํ๊ณ ์์ด์. ์์ผ๋ก 'keyof'์ ๊ด๋ จ๋ ํ์ ์ถ๋ก ๋ ๋์ฑ ๊ฐ๋ ฅํด์ง ๊ฑฐ์์. ์๋ฅผ ๋ค์ด, ๋ณต์กํ ์ ๋ค๋ฆญ ํ์ ์์๋ 'keyof'์ ๊ฒฐ๊ณผ๋ฅผ ๋ ์ ํํ๊ฒ ์ถ๋ก ํ ์ ์๊ฒ ๋ ๊ฑฐ์์.
2. ์๋ก์ด ์ ํธ๋ฆฌํฐ ํ์ ๊ณผ์ ํตํฉ
TypeScript์๋ ๊ณ์ํด์ ์๋ก์ด ์ ํธ๋ฆฌํฐ ํ์ ๋ค์ด ์ถ๊ฐ๋๊ณ ์์ด์. 'keyof'๋ ์ด๋ฐ ์๋ก์ด ์ ํธ๋ฆฌํฐ ํ์ ๋ค๊ณผ ๋ ์ ํตํฉ๋ ๊ฑฐ์์. ์๋ฅผ ๋ค์ด, ๊ฐ์ฒด์ ์ค์ฒฉ๋ ์์ฑ์ ๋ ์ฝ๊ฒ ๋ค๋ฃฐ ์ ์๋ ์๋ก์ด ์ ํธ๋ฆฌํฐ ํ์ ์ด ๋์ฌ ์๋ ์๊ฒ ์ฃ .
3. ๋ฐํ์ ์ฑ๋ฅ ์ต์ ํ
'keyof'๋ ์ปดํ์ผ ํ์์๋ง ์๋ํ์ง๋ง, ์ด๋ฅผ ํ์ฉํ ์ฝ๋ ํจํด๋ค์ด ๋ฐํ์์ ๋ฏธ์น๋ ์ํฅ๋ ๊ณ ๋ คํด์ผ ํด์. TypeScript ํ์ ์ด๋ฐ ํจํด๋ค์ด ๋ ํจ์จ์ ์ผ๋ก ๋์ํ๋๋ก ์ปดํ์ผ๋ฌ๋ฅผ ์ต์ ํํ ๊ฑฐ์์.
4. ๋ ๋์ ์ํ๊ณ ์ง์
'keyof'๋ฅผ ํ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋ ๋ง์ด ๋์ฌ ๊ฑฐ์์. ์๋ฅผ ๋ค์ด, ORM(Object-Relational Mapping) ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ 'keyof'๋ฅผ ํ์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง์ TypeScript ํ์ ์ ๋ ๊ธด๋ฐํ๊ฒ ์ฐ๊ฒฐํ ์ ์๊ฒ ์ฃ .
5. ๋ ์ง๊ด์ ์ธ ๋ฌธ๋ฒ
TypeScript ํ์ ํญ์ ๊ฐ๋ฐ์ ๊ฒฝํ์ ๊ฐ์ ํ๋ ค๊ณ ๋ ธ๋ ฅํ๊ณ ์์ด์. 'keyof'์ ๊ด๋ จ๋ ๋ฌธ๋ฒ๋ ๋ ์ง๊ด์ ์ผ๋ก ๋ฐ๋ ์ ์์ด์. ์๋ฅผ ๋ค์ด, ์ค์ฒฉ๋ ๊ฐ์ฒด์ ํค๋ฅผ ๋ ์ฝ๊ฒ ์ถ์ถํ ์ ์๋ ์๋ก์ด ๋ฌธ๋ฒ์ด ์ถ๊ฐ๋ ์๋ ์๊ฒ ์ฃ .
๐ Future Vision: ์์ผ๋ก 'keyof'๋ ๋จ์ํ ๊ฐ์ฒด์ ํค๋ฅผ ๋ค๋ฃจ๋ ๋๊ตฌ๋ฅผ ๋์ด์, ํ์ ์์คํ ์ ๋ฐ์ ์์ฐ๋ฅด๋ ํต์ฌ ์์๊ฐ ๋ ๊ฑฐ์์. ๋ฐ์ดํฐ์ ๊ตฌ์กฐ์ ๊ด๊ณ๋ฅผ ๋ ์ ํํ๊ฒ ํํํ๊ณ , ์ด๋ฅผ ๋ฐํ์ผ๋ก ๋ ์์ ํ๊ณ ํจ์จ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๊ฒ ํด์ค ๊ฑฐ๋๋๋ค. TypeScript์ ํจ๊ป ์ฑ์ฅํ๋ 'keyof'์ ๋ฏธ๋๊ฐ ์ ๋ง ๊ธฐ๋๋์ง ์๋์? ๐
์, ์ด๋ ๊ฒ 'keyof'์ ํ์ฌ์ ๋ฏธ๋์ ๋ํด ์์๋ดค์ด์. 'keyof'๋ TypeScript์ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋๋ก, ์์ผ๋ก๋ ๊ณ์ํด์ ๋ฐ์ ํด ๋๊ฐ ๊ฑฐ์์. ์ฌ๋ฌ๋ถ๋ ์ด๋ฐ ๋ฐ์ ๊ณผ์ ์ ์ฃผ๋ชฉํ๋ฉด์, 'keyof'๋ฅผ ๋ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ๊ณ์ ๊ณ ๋ฏผํด๋ณด์ธ์. ๊ทธ๋ผ ์ฌ๋ฌ๋ถ์ TypeScript ์ฝ๋๋ ๋์ฑ ๊ฒฌ๊ณ ํ๊ณ ์ ์ฐํด์ง ๊ฑฐ์์!
์ด์ ์ ๋ง 'keyof'์ ๋ํ ๋ชจ๋ ๊ฒ์ ์์๋ดค์ด์. ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ๊ณ ๊ธ ๊ธฐ๋ฒ, ์ค์ ์์ , ๊ทธ๋ฆฌ๊ณ ๋ฏธ๋ ์ ๋ง๊น์ง. ์ฌ๋ฌ๋ถ์ ์ด์ 'keyof'์ ์ง์ ํ ๋ง์คํฐ๊ฐ ๋์ จ์ด์! ๐๐๐
TypeScript์ 'keyof'๋ฅผ ํ์ฉํด ๋ ๋์ ์ฝ๋๋ฅผ ์์ฑํ์ธ์. ์ฌ๋ฌ๋ถ์ ์ฝ๋ฉ ๋ผ์ดํ๊ฐ ํ์ธต ๋ ์ฆ๊ฑฐ์์ง ๊ฑฐ์์. ํ์ด์ ๋น๋๋ค! ๐
- ์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์ฌ๋ฅ๋ท์ ์ต์ AI ๊ธฐ์ ๊ณผ ๋ฒ๋ฅ ์ ๊ธฐ๋ฐํ์ฌ ์์ฌ์ ์ง์ ์ฌ์ฐ๊ถ์ ์ ๊ทน์ ์ผ๋ก ๋ณดํธํ๋ฉฐ,
๋ฌด๋จ ์ฌ์ฉ ๋ฐ ์นจํด ํ์์ ๋ํด ๋ฒ์ ๋์์ ํ ๊ถ๋ฆฌ๋ฅผ ๋ณด์ ํฉ๋๋ค.
ยฉ 2025 ์ฌ๋ฅ๋ท | All rights reserved.
๋๊ธ 0๊ฐ