์ํฐ ํจํด
์ํฐํจํด(Anti-Pattern)์ด๋ ์ํํธ์จ์ด ๊ฐ๋ฐ, ์ค๊ณ, ๋๋ ๋ฌธ์ ํด๊ฒฐ์์ ๋นํจ์จ์ ์ด๊ณ ํด๋ก์ด ๊ดํ์ ๋งํฉ๋๋ค. ์ฝ๋๋ฅผ ์์ฑํ ๋, ๋นํจ์จ์ ์ด๊ฑฐ๋ ๋น์์ฐ์ ์ผ ์ ์๋ ๋ฐฉ์๋ค์ ์๋ฏธํฉ๋๋ค. ์ฝ๋๋ฅผ ์์ฑํ ๋น์์๋ ๋ฌธ์ ๊ฐ ์์ด๋ณด์ผ ์ ์์ง๋ง, ์๊ฐ์ด ์ง๋๊ณ ์๊ตฌ ์ฌํญ์ด ๋ฐ๋๋ฉด ์์ ์ด ๋งค์ฐ ์ด๋ ค์์ง ์ ์์ต๋๋ค.
์๋ชป๋ ๋ฐฉ์์ผ๋ก ์์ฑ๋ ์ฝ๋๋ ์คํ ์๋์ ์์คํ ์ ์ฒด ์ฑ๋ฅ์๋ ์ํฅ์ ๋ฏธ์นฉ๋๋ค.
๋ํ์ ์ธ ์ํฐ ํจํด
1. Spaghetti Code (์คํ๊ฒํฐ ์ฝ๋)
๊ตฌ์กฐ๊ฐ ์๋ ๋ณต์กํ๊ณ ๋ค์ฝํ ์ฝ๋. ์ดํดํ๊ธฐ ์ด๋ ต๊ณ , ์ ์ง๋ณด์๊ฐ ๊ฑฐ์ ๋ถ๊ฐ๋ฅํ๊ณ ์ฝ๋ ๋ณ๊ฒฝ ์ ์์์น ๋ชปํ ๋ฌธ์ ๋ฐ์์ด ๊ฐ๋ฅํฉ๋๋ค.
์์
function calculate(value) {
if (value > 10) {
if (value < 20) {
for (let i = 0; i < value; i++) {
if (i % 2 === 0) {
// ๋ณต์กํ๊ณ ๋น์ง๊ด์ ์ธ ๋ก์ง
}
}
}
}
}
2. God Object (์ ๊ฐ์ฒด)
ํ๋์ ๊ฐ์ฒด๊ฐ ๋๋ฌด ๋ง์ ์ฑ ์์ ๊ฐ์ง๋ ์ค๊ณ ๋ฐฉ์. ํ๋์ ๊ฐ์ฒด๊ฐ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ์ฌ ์ฝ๋๊ฐ ๋นํจ์จ์ ์ผ๋ก ์๋. ๋นํจ์จ์ ์ธ ์ ์ง๋ณด์ ๋ฐฉ๋ฒ์ ๋๋ค.
class AppManager {
constructor() {
this.user = null;
this.db = null;
this.cache = null;
this.api = null;
}
handleAll() {
// ๋ชจ๋ ์ผ์ ์ฒ๋ฆฌํ๋ ๊ฑฐ๋ํ ๋ฉ์๋
}
}
3. Golden Hammer (ํฉ๊ธ ๋ง์น)
ํ๋์ ๋๊ตฌ(ํ๋ ์์ํฌ, ๊ธฐ์ )๋ฅผ ๋ชจ๋ ๋ฌธ์ ์ ์ ์ฉํ๋ ค๋ ๊ฒฝํฅ. ๋ฌธ์ ์ ๋ง์ง ์๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ฉด ๋นํจ์จ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
์์
// ๋ชจ๋ ๋ฌธ์ ๋ฅผ Regular Expression์ผ๋ก ํด๊ฒฐํ๋ ค๋ ๊ฒฝ์ฐ
const regex = /some-complicated-pattern/;
if (regex.test(input)) {
// ๋๋ฌด ๊ณผ๋ํ ์ ๊ท์ ์ฌ์ฉ
}
4. Hardcoding (ํ๋์ฝ๋ฉ)
๊ฐ์ด๋ ์ค์ ์ ์ฝ๋์ ์ง์ ์ ์ผ๋ก ํ๋์ฝ๋ฉ. ํ๊ฒฝ์ด ๋ณ๊ฒฝ๋๋ฉด ํ๋์ฝ๋ฉ๋ ์ฌ๋ฌ ๊ตฐ๋ฐ์ ์ฝ๋๋ฅผ ์์ ํด์ผ ํฉ๋๋ค. ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ๋ฎ์ต๋๋ค.
์์
const apiUrl = "http://example.com/api/v1"; // ํ๋์ฝ๋ฉ๋ URL
5. Premature Optimization (์กฐ๊ธฐ ์ต์ ํ)
ํ์ํ์ง ์์ ์ต์ ํ ์์ ์ ๋๋ฌด ๋ง์ ์๊ฐ๊ณผ ์์์ ์๋น. ๋ณต์ก์ฑ์ ์ฆ๊ฐ์ํค๊ณ , ์ค์ ์ค์ํ ๋ฌธ์ ์ ์ง์คํ์ง ๋ชปํ๊ฒ ๋ฉ๋๋ค.
์์
function addNumbers(a, b) {
// ๋จ์ํ ์ฐ์ฐ์ ๊ณผ๋ํ ์ต์ ํ
return (a | 0) + (b | 0);
}
6. Singleton Overuse (์ฑ๊ธํค ๋จ์ฉ)
์ฑ๊ธํค ํจํด์ ๊ณผ๋ํ๊ฒ ์ฌ์ฉํ์ฌ ๋ชจ๋ ๊ณณ์์ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ๊ณต์ . ํ ์คํธํ๊ธฐ ์ด๋ ค์์ง๊ณ , ๋ฌธ์ ์ํฉ์ ๋ฐ๋ผ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
์์
class Database {
static instance;
static getInstance() {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
}
์ค๋ฌด์์ ์์ฃผ ๋ฐ์ํ๋ ์ฑ๋ฅ ํ๋ฝ์ ์ํฐ ํจํด
์ ์์ ์ฒ๋ผ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ ์ธก๋ฉด์์ ์ํฐํจํด๋ ๋ง์ง๋ง, ์ค๋ฌด์์ ์์ฃผ ๋ฐ์ํ๋ ์ฑ๋ฅ ํ๋ฝ ์ธก๋ฉด์ ์ํฐ ํจํด๋ ์์ต๋๋ค.
์ํฐํจํด์ ์ข ๋ฅ๋ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ง๋ง, ์ฑ๋ฅ์ ์ธ ๋ฉด(๋ถํ์ํ ์ฐ์ฐ, ๋นํจ์จ์ ์ฐ์ฐ)์ ์ด์ ์ ๋ง์ถฐ์ ์ดํด๋ณธ ์ ํ์ ๋๋ค.
์ ํ 1
๋ถํ์ํ ๋ณ์ ํ ๋น
AS/IS
const output = someData
return output;
TO/BE
return someData;
์ ํ 2
๋ถํ์ํ ํ๋กํผํฐ ํธ์ถ.
์กฐ๊ฑด๋ฌธ์์๋ obj.list.get(key) ์กฐ๊ฑด์ ๋ฐ์ง๋ฉด์ .list , get() ํจ์๋ฅผ ๋ฃฉ์
ํด์ผ ํ๋๋ฐ, ํ๋์ ๋ณ์๋ฅผ ์ ์ธํ๊ณ ๋ค๋ฃจ๋ฉด ๋ถํ์ํ ํ๋กํผํฐ ํธ์ถ์ ๋ง์ ์ ์๋ค.
AS/IS
if (obj.list.get(key)) {
const value = obj.list.get(key)
console.log(value);
}
TO/BE
const data = obj.list.get(key)
if (data) {
console.log(data);
}
์ ํ 3
arr.length ๋ฅผ ๋งค ์ธ๋ฑ์ค๋ง๋ค ํ์ธํ๊ฒ ๋ฉ๋๋ค. ๋ง์ฝ ๋ณ๊ฒฝ๋์ง ์์ ์๋ผ๋ฉด ๋ณ์์ ์ ์ธํด๋๊ณ ํ๋กํผํฐ ํธ์ถ ๋ฐ๋ณต์ ๋ง์ ์ ์์ต๋๋ค.
AS/IS
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
TO/BE
const length = arr.length; // ํ๋กํผํฐ ํธ์ถ์ ๋ฐ๋ณตํ์ง ์๋๋ก.
for (let i = 0; i < length; i++) {
console.log(arr[i]);
}
์ ํ 4
๋ถ๊ธฐ๋ฌธ ๋จ์ฉ. ๋๋ฌด ๋ง์ else if ๋ฌธ์ ์ฑ๋ฅ์ ๋ฌธ์ ์ ๊ฐ๋ ์ฑ์ ๋ฌธ์ ๋ฅผ ์ผ๊ธฐ์ํต๋๋ค. switch ๋ฌธ์ ํ์ฉํ๋ค๋ฉด Lookup Table ๋๋ Jump Table ์์ ํ๋ฒ์ ์ฐพ์ ์ ์์ต๋๋ค. ๋น์ทํ ๋ฐฉ์์ผ๋ก Map ์ ํ์ฉํ๋ฉด ๊ฐ๋ ์ฑ๊ณผ else if ๋ฅผ ๊ณ์ ํ์ธํ๋ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
AS/IS
function getLocalizedErrorMessage(errorCode: number, language: string): string {
if (language === "en") {
if (errorCode === 400) {
return "Bad Request";
} else if (errorCode === 401) {
return "Unauthorized";
} else if (errorCode === 404) {
return "Not Found";
} else {
return "Unknown Error";
}
} else if (language === "ko") {
if (errorCode === 400) {
return "์๋ชป๋ ์์ฒญ";
} else if (errorCode === 401) {
return "์ธ์ฆ๋์ง ์์";
} else if (errorCode === 404) {
return "์ฐพ์ ์ ์์";
} else {
return "์ ์ ์๋ ์ค๋ฅ";
}
} else {
return "Unknown Error"; // ์ง์๋์ง ์๋ ์ธ์ด
}
}
TO/BE
const localizedErrorMessages: Map<string, Map<number, string>> = {
en: {
400: "Bad Request",
401: "Unauthorized",
404: "Not Found",
},
ko: {
400: "์๋ชป๋ ์์ฒญ",
401: "์ธ์ฆ๋์ง ์์",
404: "์ฐพ์ ์ ์์",
},
};
function getLocalizedErrorMessage(errorCode: number, language: string): string {
return localizedErrorMessages[language]?.[errorCode] ?? "Unknown Error";
}
์ ํ 5
๋ถํ์ํ ์ธ์คํด์ค ์์ฑ.
execute({}); ์์ ๋ถํ์ํ๊ฒ {}๋ฅผ ์์ฑํ๊ณ ์์ต๋๋ค. ์ธ์คํด์ค๋ heap ์์ญ์ ๋ก๋๋๊ณ , GC ์ ์ฒ๋ฆฌ๋ฉ๋๋ค. GC ํ๋ ์์ฒด๋ ์ค๋ฒํค๋๋ฅผ ๋ฐ์์ํค๊ธฐ ๋๋ฌธ์ ๋ถํ์ํ ์ธ์คํด์ค๋ฅผ ๋๋๋ก ์์ฑํ์ง ์๋ ๊ฒ ์ข์ต๋๋ค.
AS/IS
interface IStateRequest {
info_list?: InfoListType[];
}
// ----
const state_program = ProgramBuilder.create<IStateRequest, void>(
IStateProgram,
execution_context
);
const { group_data } = state_program.execute({});
TO/BE
type RequestType = IStateRequest | undefined;
const { group_data } = state_program.execute(); // ์ ๋์จํ์
์ ํ์ฉํ์ฌ, ๋ถํ์ํ ๊ฐ์ฒด์ธ์คํด์ค ์์ฑ ๋ฐฉ์ง
์ ํ 6
๋ถํ์ํ ์ธ์คํด์ค ์์ฑ.
let resultItemJson: any = {}; ์์ ์ ์ธ๋ง ํด๋ ๋๋๋ฐ, ๋ถํ์ํ ๋น ๊ฐ์ฒด๋ฅผ ํ ๋นํ๊ณ ์์ต๋๋ค.
๋ํ else ์์๋ ๋ถํ์ํ ์ธ์คํด์ค๋ฅผ ๊ณ์ ์์ฑํ๊ณ ์์ต๋๋ค.
AS/IS
let resultItemJson: any = {};
if (state.attribute_set_sid) {
resultItemJson = JSON.parse(state.result_item_json);
} else {
resultItemJson = {
option_one: '0',
option_two: '0',
option_three: '0',
sequence_one: '1',
sequence_two: '2',
sequence_three: '3',
};
}
TO/BE
const default_result = { // ์ฑ๊ธํค ๊ฐ์ฒด
option_one: '0',
option_two: '0',
option_three: '0',
sequence_one: '1',
sequence_two: '2',
sequence_three: '3',
};
let resultItemJson: any; // ๋ณ์ ์ ์ธ๋ง
if (state.attribute_set_sid) {
resultItemJson = JSON.parse(state.result_item_json);
} else {
resultItemJson = default_result; // ์ฑ๊ธํค ๊ฐ์ฒด ์ฌํ์ฉ (์ธ๋ถ ์์ ๋ฐฉ์ง๋ฅผ ์ํด Object.freeze ๊ณ ๋ ค ๊ฐ๋ฅ)
}
์ ํ 7
๋ถ์ ์ ํ ์๋ฃ ๊ตฌ์กฐ ํ์ฉ.
์ ์์ ๊ฒฝ์ฐ ๋ฐฐ์ด์ ํตํด ์ํ๋ ๊ฐ์ ์ฐพ๋ ๋ฐฉ๋ฒ์
๋๋ค. ์ด๋ O(N) ์ ์๊ฐ๋ณต์ก๋๋ฅผ ๊ฐ์ต๋๋ค. ๋ง์ฝ Set ์๋ฃ๊ตฌ์กฐ๋ฅผ ํ์ฉํ๊ฒ ๋๋ค๋ฉด ํด์ ํจ์๋ฅผ ํตํด ๋์ฑ ๋น ๋ฅด๊ฒ ์ํ๋ ๊ฐ์ ํ์ํ ์ ์์ต๋๋ค.
AS/IS
function isAllowed(value: string): boolean {
const allowedValues = ["apple", "banana", "cherry", "date", "elderberry"];
return allowedValues.includes(value);
}
TO/BE
const allowedValues = new Set(["apple", "banana", "cherry", "date", "elderberry"]);
function isAllowed(value: string): boolean {
return allowedValues.has(value);
}
์ ํ 8
๋ถ์ ์ ํ ํจ์ ์ฌ์ฉ.
forEach ํจ์๋ ๋ด๋ถ์ ์ผ๋ก Iterator ๊ฐ์ฒด๋ฅผ ๋ฐํ ๋ฐ์ ๋ฐ๋ณต๋ฌธ์ ๋๋ฆฌ๊ฒ ๋ฉ๋๋ค. Iterator ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ๊ฒ๋ถํฐ ์ค๋ฒํค๋๋ฅผ ๋ฐ์์ํต๋๋ค. forEach๋ ๋ฐฐ์ด์ ๊ฐ ์์๋ง๋ค ์ฝ๋ฐฑ ํจ์๋ฅผ ํธ์ถํฉ๋๋ค. ํจ์ ํธ์ถ์๋ ์คํ ํ๋ ์ ์์ฑ ๋ฐ ํด์ ๋ฑ์ ์ค๋ฒํค๋๊ฐ ์์ผ๋ฏ๋ก for๋ณด๋ค ๋๋ฆด ์ ์์ต๋๋ค.
map ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ์ผ๋ฐ์ ์ธ ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ๋ฉด์ ๋ฐฐ์ด์ ์ง์ ์ ์ผ๋ก push ํ๊ณ ๋ฐฐ์ด์ ๋ฐํํด์ค๋๋ค. ์ด๋ forEach ๋ฅผ ํ์ฉํ๋ ๊ฒ๋ณด๋ค ์ฑ๋ฅ๋ฉด์์ ํจ์จ์ ์
๋๋ค.
AS/IS
const { source_list } = example_program.execute(request);
const result_list = [];
source_list.forEach((source) => {
result_list.push({
tenant_sid: source.com_code,
name: source.nm,
});
});
TO/BE
const { source_list } = example_program.execute(request);
const result_list = source_list.map((source) => {
return {
tenant_sid: source.com_code,
name: source.nm,
}
});
์ ํ 9
๋นํจ์จ์ ์ธ ๋ฌธ์์ด ๊ฒฐํฉ.
๋ฌธ์์ด์ ๊ฒฐํฉํ ๋ + ์ฐ์ฐ์๋ฅผ ํ์ฉํ๊ฒ ๋๋ฉด StringBuilder ๋ฅผ ํ์ฉํ์ฌ ์ฐ์ฐํ๋ ๊ฒ๋ณด๋ค ๋ ๋์ ์ค๋ฒํค๋์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.
AS/IS
for (let j = 1; j <= 70; j++) {
console.log("column" + j);
}
TO/BE
for (let j = 1; j <= 70; j++) {
console.log(`column${j}`);
}
์ํฐํจํด์ ํผํ๋ ๋ฐฉ๋ฒ
- SOLID ์์น ์ค์
- ๊ฐ์ฒด ์งํฅ ์ค๊ณ ์์น(SOLID)์ ๋ฐ๋ฅด๋ฉด, ์ฝ๋ ์ ์ง๋ณด์์ฑ๊ณผ ์ฌ์ฌ์ฉ์ฑ์ด ๋์์ง๊ณ ์ํฐํจํด์ ํผํ ์ ์์ต๋๋ค.
- ์ฝ๋ ๋ฆฌ๋ทฐ
- ํ์๋ค๊ณผ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ์ค๊ณ๋ ๊ตฌํ ์์ ๋ฌธ์ ์ ์ ์ฌ์ ์ ๋ฐ๊ฒฌ.
- ํ
์คํธ ์์ฑ
- ์ฝ๋๊ฐ ํน์ ์ํฉ์์ ์์๋๋ก ๋์ํ๋์ง ํ์ธํ๋ฉฐ ์ํฐํจํด ๋ฐ์ ๋ฐฉ์ง.
- ๊ธฐ์ ์ดํด ์ฌํ
- ์ฌ์ฉํ๋ ๊ธฐ์ ์ ์ฅ๋จ์ ์ ๊น์ด ์ดํดํ๊ณ ์ ์ ํ ์ ์ฉ.
- ์ ์ฐ์ฑ ํ๋ณด
- ํ๋์ฝ๋ฉ ๋์ ํ๊ฒฝ ๋ณ์, ์ค์ ํ์ผ ๋ฑ์ ์ฌ์ฉํ์ฌ ์ ์ฐ์ฑ์ ์ ์ง.
'System Design' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ (0) | 2024.11.25 |
---|
๋๊ธ