βοΈ Java Design-Pattern XXβ £ - Interpreter
βοΈ Java Design-Pattern XXβ
£ - Interpreter
π
γJAVA μΈμ΄λ‘ λ°°μ°λ λμμΈ ν¨ν΄ : μ½κ² λ°°μ°λ GoFμ 23κ°μ§ λμμΈ ν¨ν΄γ
λ₯Ό μ½κ³ μ 리ν κΈμ λλ€.
Interpreter
ν¨ν΄μ΄λ?
- νλ‘κ·Έλ¨μ΄ ν΄κ²°νλ €λ λ¬Έμ λ₯Ό κ°λ¨ν λ―Έλ μΈμ΄λ‘ ꡬννλ ν¨ν΄μ΄λ€.
- μ¦, ꡬ체μ μΈ λ¬Έμ λ₯Ό λ―Έλ μΈμ΄λ‘ μμ±λ λ―Έλ νλ‘κ·Έλ¨μΌλ‘ λ§λλ κ²μ΄λ€.
- λ―Έλ νλ‘κ·Έλ¨μ κ·Έ μ체λ‘λ λμνμ§ μκΈ° λλ¬Έμ,
Java
μΈμ΄λ‘ ν΅μνλ μν μ νλ νλ‘κ·Έλ¨μ λ§λ€μ΄ λλ€. - ν΅μ νλ‘κ·Έλ¨μ λ―Έλ μΈμ΄λ₯Ό μ΄ν΄νκ³ ν΄μνμ¬ νλ‘κ·Έλ¨μ μ€ννλ€.
- μ΄ ν΅μ νλ‘κ·Έλ¨ μ체λ₯Ό μΈν°ν리ν°λΌκ³ λΆλ₯Έλ€.
- ν΄κ²°ν΄μΌ ν λ¬Έμ κ° λ³κ²½λμ λλ
Java
μΈμ΄λ‘ μμ±λ νλ‘κ·Έλ¨μ΄ μλ λ―Έλ νλ‘κ·Έλ¨ μͺ½ μ½λλ₯Ό λ³κ²½νλ€.
λ―Έλ μΈμ΄
λ―Έλ μΈμ΄μ λͺ λ Ή
- λ―Έλ μΈμ΄λ₯Ό μ€λͺ νκΈ° μν΄ λ¬΄μ μΌλ‘ μλμ°¨λ₯Ό μ‘°μ’ νλ μΈμ΄λ₯Ό μκ°ν΄λ³΄μ.
- μλμ°¨ μ‘°μ’ μ΄λΌμ§λ§ ν μ μλ μΌμ λ€μ μΈ κ°μ§ λΏμ΄λ€.
go
: μμΌλ‘ 1λ―Έν° μ΄λνλ€.right
: μ€λ₯Έμͺ½μΌλ‘ λλ€.left
: μΌμͺ½μΌλ‘ λλ€.
- μ΄κ²λ§μΌλ‘λ μμνλ λ°λ³΅ λͺ λ Ήλ μΆκ°νμ.
repeat
: λ°λ³΅νλ€.
λ―Έλ νλ‘κ·Έλ¨μ μ
1
program go end
- μμ κ°μ΄ νλ‘κ·Έλ¨μ μμκ³Ό λμ μ μ μλλ‘
program
κ³Όend
λ¨μ΄λ‘ λͺ λ Ήμ κ°μΌλ€.
1
program go right right go end
- μκΈ° μ½λλ μμΌλ‘ κ°λ€κ° λ€μ λ€λ‘ λμμ μ€λ λ―Έλ νλ‘κ·Έλ¨μ΄λ€.
1
2
program go right go right go right go right end
program repeat 4 go right end end
- μκΈ° μ½λλ μ μ¬κ°νμ κ·Έλ¦¬κ³ λμμ€λ λ―Έλ νλ‘κ·Έλ¨μ΄λ€.
1
program repeat 4 repeat 3 go right go left end right end end
- μκΈ° μ½λλ λ§λ¦λͺ¨λ₯Ό κ·Έλ¦¬κ³ λμμ€λ λ―Έλ νλ‘κ·Έλ¨μ΄λ€.
λ―Έλ μΈμ΄μ λ¬Έλ²
1
2
3
4
5
<program> ::= program <command List>
<command list> ::= <command>* end
<command> ::= <repeat command> | <primitive command>
<repeat command> ::= repeat <number> <command list>
<primitive command> ::= go | right | left
BNC
νκΈ°λ²μ μ¬μ©νλ€.Backus-Naur Form
λλBackus Normal Form
μ μ€μλ§λ‘ μΈμ΄μ λ¬Έλ²μ νκΈ°ν λ λ§μ΄ μ¬μ©λλ€.
1
<program> ::= program <command list>
- νλ‘κ·Έλ¨μ΄λΌλ
<program>
μ μ μνλ€. - ν€μλ λ€λ‘ λͺ
λ Ή λͺ©λ‘
<command list>
κ° μ΄μ΄μ§ κ²μΌλ‘ μ μνλ€. ::=
λ μ’λ³μ΄ μ μλλ λμμ΄κ³ , μ°λ³μ΄ μ μλλ λ΄μ©μ΄λ€.
1
<command list> ::= <command>* end
- μ¬κΈ°μλ λͺ
λ Ή λͺ©λ‘
<command list>
λ₯Ό μ μνλ€. <command list>
λ<command>
κ° 0κ° μ΄μ λ°λ³΅λ νend
λ¨μ΄κ° μ€λ κ²μΌλ‘ μ μνλ€.*
λ μ§μ μ λͺ λ Ήμ΄κ° 0ν μ΄μ λ°λ³΅λ¨μ μλ―Ένλ€.
1
<command> ::= <repeat command> | <primitive command>
- μ΄λ²μλ λͺ
λ Ή
<command>
λ₯Ό μ μνλ€. <command>
λ<repeat command>
λλ<primitive command>
μ€ νλλ‘ μ μλλ€.
1
<repeat command> ::= repeat <number> <command list>
- λ€μμΌλ‘ λ°λ³΅ λͺ
λ Ή
<repeat command>
λ₯Ό μ μνλ€. repeat
μ΄λΌλ λͺ λ Ήμ΄ λ€μ λ°λ³΅ νμ<number>
κ° μ€κ³<command list>
κ° λ€λ°λ₯΄λ κ²μΌλ‘ μ μνλ€.- λͺ
λ Ή λͺ©λ‘
<command list>
λ μ΄λ―Έ μμμ μ μλμλ€. <command list>
μ μ μμλ<command>
κ° μ¬μ©λκ³ ,<command>
μ μ μμμλ<repeat command>
κ° μ¬μ©λκ³ ,<repeat command>
μ μμμλ<command list>
κ° μ¬μ©λλ μ΄λ¬ν ννλ₯Ό μ¬κ·μ μΈ μ μλΌκ³ νλ€.
1
<primitive command> ::= go | right | left
- κΈ°λ³Έ λͺ
λ Ή
<primitive command>
μ μ μνλ€.
ν°λ―Έλ μ΅μ€νλ μ κ³Ό λ Όν°λ―Έλ μ΅μ€νλ μ
go
,left
,right
μ²λΌ λ μ κ°λμ§ μλ ννμ ν°λ―Έλ μ΅μ€νλ μ μ΄λΌκ³ νλ€.- λ²μ€λ μ΄μ°¨μ μ’ μ°©μμ ν°λ―Έλμ΄λΌκ³ νλ κ²κ³Ό λΉμ·νλ€.
- λν
program
μ΄λrepeat
λͺ λ Ήμ΄μ²λΌ λ€μ λ μ κ°λλ ννμ λ Όν°λ―Έλ μ΅μ€νλ μ μ΄λΌκ³ νλ€.
μμ νλ‘κ·Έλ¨
- λ€μμ λ―Έλ μΈμ΄λ₯Ό ꡬ문 ν΄μνλ μμ νλ‘κ·Έλ¨μ΄λ€.
- λ¨μν λ¬Έμμ΄μΈ λ―Έλ νλ‘κ·Έλ¨μ λΆν΄νμ¬ κ° λΆλΆμ΄ μ΄λ€ κ΅¬μ‘°λ‘ λμ΄ μλμ§λ₯Ό ν΄μνλ κ²μ΄ ꡬ문 ν΄μμ΄λ€.
- κ°λ Ή λ€μ λ―Έλ νλ‘κ·Έλ¨μ΄ μ£Όμ΄μ‘λ€κ³ νμ.
1
program repeat 4 go right end end
- μ μ΄λ―Έμ§μ κ°μ ꡬ문 νΈλ¦¬ ꡬ쑰λ₯Ό λ©λͺ¨λ¦¬ μμ λ§λ€μ΄ λ΄λ μ²λ¦¬κ° ꡬ문 ν΄μμ΄λ€.
Node
ν΄λμ€
1
2
3
public abstract class Node {
public abstract void parse(Context context) throws ParseException;
}
- ꡬ문 νΈλ¦¬μ κ° λ Έλλ₯Ό ꡬμ±νλ μ΅μμ ν΄λμ€μ΄λ€.
- ν΄λΉ ν΄λμ€μλ μΆμ λ©μλ
parse()
λ§ μ μΈλμ΄ μλ€. - μ΄λ ꡬ문 ν΄μ μ²λ¦¬λ₯Ό μν λ©μλμ΄λ€.
- μΈμλ‘ μ λ¬λλ
Context
λ μν©μ λνλ΄λλ°, μ΄νμ λ±μ₯νλ€. - μ΄ λ©μλμλ ꡬ문 ν΄μμ νλ€κ° μ€λ₯κ° λ¬μ λ
ParseException
μμΈλ₯Ό λμ§λ€.
ProgramNode
ν΄λμ€
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* <program> ::= program <command list> */
public class ProgramNode extends Node {
private Node commandListNode;
@Override
public void parse(Context context) throws ParseException {
context.skipToken("program");
commandListNode = new CommandListNode();
commandListNode.parse(context);
}
@Override
public String toString() {
return "[program " + commandListNode + "]";
}
}
<program>
μ λνλ΄λprogramNode
ν΄λμ€λ€.- μ΄ ν΄λμ€μλ
Node
νcommandListNode
νλκ° μλ€. - μ΄ νλλ μμ μ λ€μ μ΄μ΄μ§λ
<command list
μ λμνλ λ Έλλ₯Ό μ μ₯νκΈ° μν¨μ΄λ€. ProgramNode
μparse()
λ©μλμ 첫 λΌμΈμμλ"program"
μ΄λΌλ λ¨μ΄λ₯Ό 건λ λ΄λ€.- ꡬ문 ν΄μμ ν λ μ²λ¦¬ λ¨μλ₯Ό ν ν°μ΄λΌκ³ νλ€.
- μ’ λ μμΈν λ§νμλ©΄, μ΄ν λΆμμ λ¬Έμλ‘λΆν° ν ν°μ λ§λ€κ³ , ꡬ문 ν΄μμ ν ν°μΌλ‘λΆν° ꡬ문 νΈλ¦¬λ₯Ό λ§λ λ€.
BNF
λ₯Ό 보면 κ·Έ λ€λ‘<command list>
κ° μ΄μ΄μ§λ€.<command list>
μ λμνλCommandListNode
μΈμ€ν΄μ€λ₯Ό μμ±νκ³ , κ·Έ μΈμ€ν΄μ€μparse()
λ©μλλ₯Ό νΈμΆνλ€.<command list>
κ° μ΄λ ν λ΄μ©μΌλ‘ λμ΄ μλμ§λProgramNode
λ©μλμλ κΈ°μ λμ΄ μμ§ μλ€.ProgramNode
μ κΈ°μ νλ κ²μ μ΄λκΉμ§λ λ€μκ³Ό κ°μ΄BNF
μμ 보μ΄λ λ²μμ νμ λλ€.
1
<program> ::= program <command list>
CommandListNode
ν΄λμ€
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.ArrayList;
import java.util.List;
/* <command list> ::= <command>* end */
public class CommandListNode extends Node {
private List<Node> list = new ArrayList<>();
@Override
public void parse(Context context) throws ParseException {
while (true) {
if (context.currentToken() == null) {
throw new ParseException("Error: Missing 'end'");
} else if (context.currentToken().equals("end")) {
context.skipToken("end");
break;
} else {
Node commandNode = new CommandNode();
commandNode.parse(context);
list.add(commandNode);
}
}
}
@Override
public String toString() {
return list.toString();
}
}
<command>
κ° 0ν μ΄μ λ°λ³΅νλ ννλ₯Ό κ°κ³ μjava.util.List<Node>
ν νλλ₯Ό κ°μ§κ³ μλ€.parse()
λ©μλλ λ€μκ³Ό κ°λ€.
- νμ¬ μ£Όλͺ©νκ³ μλ ν ν°
context.curruntToken()
κ°μ΄null
μ΄λ©΄ λλ λ¨μ ν ν°μ΄ μλ€λ λ§μ΄λ€. μ΄ κ²½μ°parse()
λend
λͺ λ Ήμ΄κ° μλ€λ λ©μμ§λ₯Ό λΆμ¬ μμΈλ₯Ό λμ§λ€. - νμ¬ μ£Όλͺ©νκ³ μλ ν ν°μ΄
end
λ©΄ λ°λ³΅λ¬Έμ νμΆνλ€. - νμ¬ μ£Όλͺ©νκ³ μλ ν ν°μ΄
end
κ° μλλ©΄ κ·Έκ²μ<command>
λΌλ μλ―Έλ‘,CommandNode
μΈμ€ν΄μ€λ₯Ό λ§λ€μ΄parse()
νλ€. μ΄ν μΈμ€ν΄μ€λ₯Όlist
νλμ μΆκ°νλ€.
- μ¬κΈ°μλ
BNF
λ‘ κΈ°μ λ λ²μμμλ§ μ²λ¦¬νλ κ²μ μ μ μλ€. - μ΄λ κ² νλ©΄ νλ‘κ·Έλ¨μ μ€μκ° μ€μ΄λ λ€.
- 무μ¬μ½ βμ΄λ κ² νλ©΄ μλκ° λ λΉ¨λΌμ§μ§ μμκΉ?βλΌλ μ νΉμ μ¬λ‘μ‘ν λ μμΈν ꡬ쑰κΉμ§ μ½λ μ²λ¦¬λ₯Ό νλ€λ©΄ μκ°νμ§ λͺ»ν λ²κ·Έλ₯Ό λ§λ€μ΄ λΌ μ μλ€.
interpreter
ν¨ν΄μ μλ λ―Έλ μΈμ΄λΌλ κ°μ μ μΈ μ²λ¦¬ λ°©λ²μ μ΄μ©νλ―λ‘ μμ¬μ£Όλ‘ ν¨μ¨μ κΎνλ κ²μ κ·Έλ€μ§ νλͺ ν λ°©λ²μ΄ μλλ€.
CommandNode
ν΄λμ€
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* <command> ::= <repeat command> | <primitive command> */
public class CommandNode extends Node {
private Node node;
@Override
public void parse(Context context) throws ParseException {
if (context.currentToken().equals("repeat")) {
node = new RepeatCommandNode();
node.parse(context);
} else {
node = new PrimitiveCommandNode();
node.parse(context);
}
}
@Override
public String toString() {
return node.toString();
}
}
Node
ν νλnode
λ<repeat command>
μ λμνλRepeatCommandNode
λλ<primitive command>
μ λμνλPrimitiveCommandNode
ν΄λμ€μ μΈμ€ν΄μ€λ₯Ό μ μ₯νκΈ° μν΄ μ¬μ©λλ€.
RepeatCommandNode
ν΄λμ€
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* <repeat command> ::= repeat <number> <command list> */
public class RepeatCommandNode extends Node {
private int number;
private Node commandListNode;
@Override
public void parse(Context context) throws ParseException {
context.skipToken("repeat");
number = context.currentNumber();
context.nextToken();
commandListNode = new CommandListNode();
commandListNode.parse(context);
}
@Override
public String toString() {
return "[repeat " + number + " " + commandListNode + "]";
}
}
<number>
λΆλΆμnumber
νλ,<command list>
λΆλΆμcommandListNode
νλμ μ μ₯λλ€.parse()
λ©μλλ λ€μκ³Ό κ°μ΄ μ¬κ·μ±μ λλ€.
RepeatCommandNode
μparse()
λ©μλ μμμλCommandListNode
μ μΈμ€ν΄μ€λ₯Ό λ§λ€μ΄parse()
λ©μλλ₯Ό νΈμΆνλ€.CommandListNode
μparse()
λ©μλ μμμλCommandNode
μ μΈμ€ν΄μ€λ₯Ό λ§λ€μ΄parse()
λ©μλλ₯Ό νΈμΆνλ€.CommandNode
μparse()
λ©μλ μμμλRepeatCommandNode
μ μΈμ€ν΄μ€λ₯Ό λ§λ€μ΄parse()
λ©μλλ₯Ό νΈμΆνλ€.
- μμ μ¬κ· μ²λ¦¬λ μΈμ κ°
PrimitiveCommandNode
λ₯Ό λ§λκΈ° μ κΉμ§ λ°λ³΅λλ€. PrimitiveCommandNode
κ° λ°λ‘ ν°λ―Έλ μ΅μ€νλ μ μ΄λ€.- μ¬κ·μ μΈ μ·¨κΈμ μ΅μνμ§ μλ€λ©΄, μ μ§ λ¬΄ν 루νκ° λ κ² κ°μ§λ§, κ·Έκ²μ μ°©κ°μ΄λ€.
- ν°λ―Έλ μ΅μ€νλ μ μ μμν λλ¬νμ§ μλλ€λ©΄ κ·Έ μ μλ μλͺ»λ κ²μ΄λ€.
PrimitiveCommandNode
ν΄λμ€
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* <primitive command> ::= go | right | left */
public class PrimitiveCommandNode extends Node {
private String name;
@Override
public void parse(Context context) throws ParseException {
name = context.currentToken();
if (name == null) {
throw new ParseException("Error: Missing <primitive command>");
} else if (!name.equals("go") && !name.equals("right") && !name.equals("left")) {
throw new ParseException("Error: Unknown <primitive command>: '" + name + "'");
}
context.skipToken(name);
}
@Override
public String toString() {
return name;
}
}
- μ΄ ν΄λμ€μ
parse()
μμλ λ€λ₯Έparse()
λ©μλλ₯Ό νΈμΆνμ§ μλλ€.
Context
ν΄λμ€
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import java.util.*;
public class Context {
private String[] tokens;
private String lastToken;
private int index;
public Context(String text) {
this.tokens = text.split("\\s+");
this.index = 0;
nextToken();
}
public String nextToken() {
if (index < tokens.length) {
lastToken = tokens[index++];
} else {
lastToken = null;
}
return lastToken;
}
public String currentToken() {
return lastToken;
}
public void skipToken(String token) throws ParseException {
if (currentToken() == null) {
throw new ParseException("Error: '" + token + "' is expected, but no more token is found.");
} else if (!token.equals(currentToken())) {
throw new ParseException("Error: '" + token + "' is expected, but '" + currentToken() + "' is found.");
}
nextToken();
}
public int currentNumber() throws ParseException {
if (currentToken() == null) {
throw new ParseException("Error: No more token.");
}
int number = 0;
try {
number = Integer.parseInt(currentToken());
} catch (NumberFormatException e) {
throw new ParseException("Error: " + e);
}
return number;
}
}
- ꡬ문 ν΄μμ μν΄ νμν λ©μλλ₯Ό μ 곡νλ€.
nextToken()
: λ€μ ν ν°μ μ»λλ€.currentToken()
: νμ¬ ν ν°μ μ»λλ€.skipToken()
: νμ¬ ν ν°μ 체ν¬νκ³ μ, λ€μ ν ν°μ μ»λλ€.currentToken()
: νμ¬ ν ν°μ μμΉλ‘ μ»λλ€.
- μ£Όμ΄μ§ λ¬Έμμ΄λ‘λΆν° 곡백 λ¬Έμκ° ν κ° μ΄μ μ°μλ κ²μ ꡬλΆνμ¬ ν ν° λ°°μ΄μ μμ±νλ€.
text.split("\\s+")
λΌμΈμ΄ μ΄μ ν΄λΉνλ€.
ParseException
ν΄λμ€
1
2
3
4
5
public class ParseException extends Exception {
public ParseException(String msg) {
super(msg);
}
}
- λ¨μν μμΈ μ²λ¦¬λ₯Ό μν ν΄λμ€μ΄λ€.
Main
ν΄λμ€
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) {
try {
for (String text: Files.readAllLines(Path.of("program.txt"))) {
System.out.println("text = \"" + text + "\"");
Node node = new ProgramNode();
node.parse(new Context(text));
System.out.println("node = " + node);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Main
μprogram.txt
λΌλ νμΌμ μ½μ΄ νμ€νμ€ λ―Έλ νλ‘κ·Έλ¨μΌλ‘ μκ°νκ³ κ΅¬λ¬Έμ λΆμνμ¬ κ²°κ³Όλ₯Ό λ¬Έμμ΄λ‘ νμνλ€.program.txt
νμΌ λ΄μ©μ λ€μκ³Ό κ°λ€.
program end
program go end
program go right go right go right go right end
program repeat 4 go right end end
program repeat 4 repeat 3 go right go left end right end end
Interpreter
ν¨ν΄μ λ±μ₯μΈλ¬Ό
Node
ν΄λμ€
- ꡬ문 νΈλ¦¬μ λ
Έλ μΈν°νμ΄μ€λ₯Ό μ μνλ μΆμ νν(
AbstractExpression
) μμ λ§‘μλ€.
PrimitiveCommandNode
ν΄λμ€
BNF
μ ν°λ―Έλ μ΅μ€νλ μ μ λμνλ μ’ λ¨ νν(TerminalExpression
) μμ λ§‘μλ€.
ProgramNode
, CommandNode
, RepeatCommandNode
, CommandListNode
ν΄λμ€
BNF
μ λ Όν°λ―Έλ μ΅μ€νλ μ μ λμνλ λΉμ’ λ¨ νν(NonterminalExpression
) μμ λ§‘μλ€.
Context
ν΄λμ€
- μΈν°ν리ν°κ° ꡬ문 ν΄μμ νκΈ° μν μ 보λ₯Ό μ 곡νλ λ¬Έλ§₯(
Context
) μμ λ§‘μλ€.
Main
ν΄λμ€
- ꡬ문 νΈλ¦¬λ₯Ό 쑰립νκΈ° μν΄
TerminalExpression
μNonterminalExpression
λ₯Ό νΈμΆνλ μλ’°μ(Client
) μμ λ§‘μλ€.
μ± μμ μ μνλ ννΈ
κ·Έ μΈμ μ΄λ€ λ―Έλ μΈμ΄κ° μμκΉ?
- μ΄ μ₯μμλ 무μ μΌλ‘ μλμ°¨λ₯Ό μ‘°μ’ νλ λ―Έλ μΈμ΄λ₯Ό νμ΅νλ€.
- λͺ κ°μ§ μλ₯Ό λ€μ΄λ³΄μ.
- μ κ· νν:
raining & (dogs | cats) *
βraining
λ€μcat
λλdog
μ΄ 0λ² μ΄μ λ°λ³΅ - κ²μ ꡬ문:
site: example.com "κ²μμ΄1" AND "κ²μμ΄2"
βexample.com
μμ κ²μμ΄1κ³Ό κ²μμ΄2μ μμ ν μΌμΉνλ κ²μμ νν - μΌκ΄ μ²λ¦¬ μΈμ΄: κΈ°λ³Έμ μΈ λͺ
λ Ήμ΄ λͺ κ° μ€λΉλμ΄ μκ³ , κ·Έ λͺ
λ Ήμ μμλλ‘ μ€ννκ±°λ λ°λ³΅νλ μΈμ΄λ
Interpreter
ν¨ν΄μΌλ‘ μ²λ¦¬ν μ μλ€. μ΄ μ₯μ 무μ μ‘°μ’ μ μ΄λ μΌκ΄ μ²λ¦¬ μΈμ΄μ μΌμ’ μ΄λΌκ³ ν μ μλ€.
건λλΈ κ²μΈκ° μ½μ κ²μΈκ°?
- μΈν°ν리ν°λ₯Ό λ§λ€ λ μμ£Ό μΌμ΄λλ κ²μ΄ ν ν°μ ν κ° λ μ½κ±°λ λͺ» μ½λ λ²κ·Έμ΄λ€.
- κ° λ Όν°λ―Έλμ λμνλ λ©μλλ₯Ό μΈ λλ νμ μ΄ λ©μλμ μμ λ μ΄λκΉμ§ ν ν°μ μ½μλμ§, μ΄ λ©μλμμ λμ¬ λ μ΄λκΉμ§ ν ν°μ μ½μ΄μΌ νλμ§ μ κ²½ μ¨μΌ νλ€.
This post is licensed under CC BY 4.0 by the author.