Post

⛓️ Java Design-Pattern XXβ…‘ - Proxy

⛓️ Java Design-Pattern XXβ…‘ - Proxy

πŸ“— γ€ŽJAVA μ–Έμ–΄λ‘œ λ°°μš°λŠ” λ””μžμΈ νŒ¨ν„΄ : μ‰½κ²Œ λ°°μš°λŠ” GoF의 23κ°€μ§€ λ””μžμΈ νŒ¨ν„΄γ€λ₯Ό 읽고 μ •λ¦¬ν•œ κΈ€μž…λ‹ˆλ‹€.

Proxy νŒ¨ν„΄μ΄λž€?

  • Proxyλž€ λŒ€λ¦¬μΈμ΄λΌλŠ” 뜻으둜, 일을 ν•΄μ•Ό ν•  본인을 λŒ€μ‹ ν•˜λŠ” μ‚¬λžŒμ΄λ‹€.
  • 객체지ν–₯ ν”„λ‘œκ·Έλž¨μ—μ„œλŠ” 본인도, λŒ€λ¦¬μΈλ„ 객체가 λœλ‹€.
  • λ°”λΉ μ„œ κ·Έ 일을 ν•  수 μ—†λŠ” 본인 객체λ₯Ό λŒ€μ‹ ν•΄μ„œ λŒ€λ¦¬μΈ 객체가 μ–΄λŠ 정도 일을 μ²˜λ¦¬ν•œλ‹€.

예제 ν”„λ‘œκ·Έλž¨

  • 이번 예제 ν”„λ‘œκ·Έλž¨μ€ β€˜μ΄λ¦„ 뢙인 프린터’이닀.
  • Main ν΄λž˜μŠ€λŠ” PrinterProxy μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜μ—¬ 이름을 뢙이고 κ·Έ 이름을 ν‘œμ‹œν•œλ‹€.
  • 이름 μ„€μ •μ΄λ‚˜ 취득에 λŒ€ν•΄μ„œλŠ” μ§„μ§œ Printer μΈμŠ€ν„΄μŠ€λŠ” μƒμ„±λ˜μ§€ μ•Šμ•˜λ‹€.
  • μ‹€μ œλ‘œ ν”„λ¦°ν„°ν•˜λŠ” 단계가 λ˜μ–΄μ„œμ•Ό PrinterProxy ν΄λž˜μŠ€λŠ” Printer μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•œλ‹€.
  • Printer ν΄λž˜μŠ€μ™€ PrinterProxy 클래슀λ₯Ό λ™μΌμ‹œν•˜κΈ° μœ„ν•΄ Printableμ΄λΌλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν–ˆλ‹€.
  • μ—¬κΈ°μ„œλŠ” Printer μΈμŠ€ν„΄μŠ€ 생성에 였랜 μ‹œκ°„μ΄ κ±Έλ¦°λ‹€λŠ” μ „μ œλ‘œ ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•œλ‹€.
  • λ¬Όλ‘  예제 ν”„λ‘œκ·Έλž¨μΌ λΏμ΄λ―€λ‘œ 5초 μ •λ„μ˜ μ‹œκ°„μ„ 벌 뿐이닀.

Printer 클래슀

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
public class Printer implements Printable {
	private String name; // 이름
	
	/* μƒμ„±μž */
	public Printer() {
		heavyJob("Printer μΈμŠ€ν„΄μŠ€ 생성 쀑");
	}
	
	/* μƒμ„±μž(이름 μ§€μ •) */
	public Printer(String name) {
		this.name = name;
		heavyJob("Printer μΈμŠ€ν„΄μŠ€(" + name + ") 생성 쀑");
	}
	
	/* 이름 μ„€μ • */
	@Override
	public void setPrinterName(String name) {
		this.name = name;
	}
	
	/* 이름 취득 */
	@Override
	public String getPrinterName() {
		return name;
	}
	
	/* 이름 λΆ™μ—¬μ„œ ν‘œμ‹œ */
	@Override
	public void print(String string) {
		System.out.println("=== " + name + " ===");
		System.out.println(string);
	}
	
	/* 무거운 μž‘μ—…μ΄λΌκ³  κ°€μ • */
	private void heavyJob(String msg) {
		System.out.print(msg);
		
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) { }
			System.out.print(".");
		}
		
		System.out.println("μ™„λ£Œ");
	}
}
  • μ•žμ„œ λ§ν•œ κ²ƒμ²˜λŸΌ κ°€μ§œ 무거운 μž‘μ—…μΈ heavyJob() λ©”μ„œλ“œλ₯Ό κ°–κ³  μžˆλ‹€.
  • Proxy νŒ¨ν„΄μ˜ 핡심은 Printer ν΄λž˜μŠ€κ°€ μ•„λ‹Œ PrinterProxy ν΄λž˜μŠ€μ— μžˆλ‹€.

Printable μΈν„°νŽ˜μ΄μŠ€

1
2
3
4
5
public interface Printable {
	public abstract void setPrinterName(String name);
	public abstract String getPrinterName();
	public abstract void print(String string);
}
  • Printer ν΄λž˜μŠ€μ™€ PrinterProxy ν΄λž˜μŠ€λŠ” λ™μΌμ‹œν•˜κΈ° μœ„ν•œ 것이닀.
  • 각각 이름 μ„€μ •, 이름 취득, ν”„λ¦°νŠΈ 아웃을 μœ„ν•œ λ©”μ„œλ“œμ΄λ‹€.

PrinterProxy 클래슀

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
public class PrinterProxy implements Printable {
	private String name;  // 이름
	private Printer real; // 본인
	
	/* μƒμ„±μž */
	public PrinterProxy() {
		this.name = "No Name";
		this.real = null;
	}
	
	/* μƒμ„±μž(이름 μ§€μ •) */
	public PrinterProxy(String name) {
		this.name = name;
		this.real = null;
	}
	
	/* 이름 μ„€μ • */
	@Override
	public synchronized void setPrinterName(String name) {
		if (real != null) {
			// '본인'μ—κ²Œλ„ μ„€μ •ν•œλ‹€
			real.setPrinterName(name);
		}
		this.name = name;
	}
	
	/* 이름 취득 */
	@Override
	public String getPrinterName() {
		return name;
	}
	
	/* ν‘œμ‹œ */
	@Override
	public void print(String string) {
		realize();
		real.print(string);
	}
	
	/* 본인 생성 */
	private synchronized void realize() {
		if (real == null) {
			real = new Printer(name);
		}
	}
}
  • Printer 클래슀의 λŒ€λ¦¬μΈ 역할을 ν•˜λ©°, Printable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œλ‹€.
  • name ν•„λ“œλŠ” 이름을 μ €μž₯ν•˜κ³  real ν•„λ“œλŠ” 본인을 μ €μž₯ν•œλ‹€.
  • setPrinterName() λ©”μ„œλ“œλŠ” μƒˆλ‘œ 이름을 μ„€μ •ν•œλ‹€.
  • real이 null이면 λ‹¨μˆœνžˆ 이름을 μ„€μ •ν•˜κ³ , μ•„λ‹ˆλΌλ©΄ 본인에 λŒ€ν•΄μ„œλ„ κ·Έ 이름을 μ„€μ •ν•œλ‹€.
  • getPrinterName() λ©”μ„œλ“œλŠ” μžμ‹ μ˜ name ν•„λ“œ 값을 λ°˜ν™˜ν•œλ‹€.
  • print() λ©”μ„œλ“œλŠ” λŒ€λ¦¬μΈμ΄ ν•  수 μžˆλŠ” λ²”μœ„λ₯Ό λ„˜μ–΄μ„œλŠ” 처리이기 λ•Œλ¬Έμ—, realize() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ 본인을 μƒμ„±ν•œλ‹€.
  • real ν•„λ“œμ—λŠ” 본인이 μ €μž₯λ˜μ–΄ 있기 λ•Œλ¬Έμ— real.print() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.
  • 이λ₯Ό μœ„μž„μ΄λΌκ³  ν•œλ‹€.
  • setPrinterName(), getPrintName() λ©”μ„œλ“œλ₯Ό μ—¬λŸ¬ 번 ν˜ΈμΆœν•΄λ„ Printer μΈμŠ€ν„΄μŠ€λŠ” μƒμ„±λ˜μ§€ μ•Šκ³ , Printer μΈμŠ€ν„΄μŠ€ 본인이 μ •λ§λ‘œ ν•„μš”ν•  λ•Œ μƒμ„±λœλ‹€.
  • realize() λ©”μ„œλ“œλŠ” λ‹¨μˆœνžˆ real ν•„λ“œκ°€ null 이면 new ν‚€μ›Œλ“œλ₯Ό 톡해 μƒμ„±ν•˜κ³ , μ•„λ‹ˆλΌλ©΄ 아무 일도 ν•˜μ§€ μ•ŠλŠ”λ‹€.
  • 기얡해두어야 ν•˜λŠ” 것은 Printer ν΄λž˜μŠ€λŠ” PrinterProxy의 쑴재λ₯Ό λͺ¨λ₯Έλ‹€λŠ” 점이닀.
  • μžμ‹ μ΄ PrinterProxyλ₯Ό κ²½μœ ν•΄μ„œ ν˜ΈμΆœλ˜λŠ”μ§€, 직접 ν˜ΈμΆœλ˜λŠ”μ§€ Printer ν΄λž˜μŠ€λŠ” λͺ¨λ₯Έλ‹€.
  • 반면 PrinterProxy ν΄λž˜μŠ€λŠ” Printer 클래슀λ₯Ό μ•Œκ³  μžˆλ‹€.
  • 즉 PrinterProxy ν΄λž˜μŠ€λŠ” Printer ν΄λž˜μŠ€μ™€ κ³ μ •μ μœΌλ‘œ κ²°ν•©λœ λΆ€ν’ˆμΈ 것이닀.
  • λ˜ν•œ λ©€ν‹° μ“°λ ˆλ“œ ν™˜κ²½μ„ κ³ λ €ν•΄ setPrinterName(), realize() λ©”μ„œλ“œλŠ” synchronized둜 μ„ μ–Έλ˜μ–΄ μžˆλ‹€.

Main 클래슀

1
2
3
4
5
6
7
8
9
public class Main {
	public static void main(String[] args) {
		Printable p = new PrinterProxy("Alice");
		System.out.println("이름은 ν˜„μž¬ " + p.getPrinterName() + "μž…λ‹ˆλ‹€.");
		p.setPrinterName("Bob");
		System.out.println("이름은 ν˜„μž¬ " + p.getPrinterName() + "μž…λ‹ˆλ‹€.");
		p.print("Hello, world.");
	}
}
  • PrinterProxy 클래슀λ₯Ό κ²½μœ ν•΄μ„œ Printer 클래슀λ₯Ό μ΄μš©ν•˜λŠ” ν΄λž˜μŠ€μ΄λ‹€.
  • Printer μΈμŠ€ν„΄μŠ€λŠ” print() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œ 후에 μƒμ„±λœλ‹€.

Proxy νŒ¨ν„΄μ˜ λ“±μž₯인물

Printable μΈν„°νŽ˜μ΄μŠ€

  • Proxy와 RealSubjectλ₯Ό λ™μΌμ‹œν•˜κΈ° μœ„ν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•˜λŠ” 본인(Subject) 역을 λ§‘μ•˜λ‹€.
  • ClientλŠ” Proxy와 RealSubjectλ₯Ό ꡬ별할 ν•„μš”κ°€ μ—†λ‹€.

PrinterProxy 클래슀

  • Client의 μš”μ²­μ„ μ΅œλŒ€ν•œ μ²˜λ¦¬ν•˜κ³ , μ •λ§λ‘œ RealSubjectκ°€ ν•„μš”ν•  λ•Œ RealSubjectλ₯Ό μƒμ„±ν•˜λŠ” λŒ€λ¦¬μΈ(Proxy) 역을 λ§‘μ•˜λ‹€.
  • Subject에 μ •μ˜λœ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œλ‹€.

Printer 클래슀

  • Proxy만으둜 감당할 수 없을 λ•Œ μƒμ„±λ˜λŠ” μ‹€μ œ 본인(RealSubject) 역을 λ§‘μ•˜λ‹€.
  • 이 역할도 Subject에 μ •μ˜λœ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œλ‹€.

Main 클래슀

  • Proxy νŒ¨ν„΄μ„ μ΄μš©ν•˜λŠ” Client 역을 λ§‘μ•˜λ‹€.

μ±…μ—μ„œ μ œμ‹œν•˜λŠ” 힌트

λŒ€λ¦¬μΈμ„ μ‚¬μš©ν•΄ 속도 올리기

  • 예제 ν”„λ‘œκ·Έλž¨μ—μ„œλŠ” Proxyλ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨ μ‹€μ œλ‘œ print() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  λ•ŒκΉŒμ§€ 무거운 처리λ₯Ό μ΅œλŒ€ν•œ 늦좜 수 μžˆμ—ˆλ‹€.
  • μ΄ˆκΈ°ν™”μ— μ‹œκ°„μ΄ κ±Έλ¦¬λŠ” κΈ°λŠ₯이 λ§Žμ€ λŒ€κ·œλͺ¨ μ‹œμŠ€ν…œμ„ 생각해보면 와 닿을 것이닀.
  • κ°€λ Ή μ‹œμž‘ μ‹œμ μ— μ΄μš©ν•˜μ§€ μ•ŠλŠ” κΈ°λŠ₯κΉŒμ§€ μ „λΆ€ μ΄ˆκΈ°ν™”ν•΄λ²„λ¦¬λ©΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹œμž‘ μ‹œκ°„μ΄ κΈΈμ–΄μ§„λ‹€.
  • μ‹€μ œλ‘œ κ·Έ κΈ°λŠ₯을 μ‚¬μš©ν•˜λŠ” 단계가 λ˜μ—ˆμ„ λ•Œ μ΄ˆκΈ°ν™”ν•˜λŠ” 편이 κ·Έ μ‚¬μš©μžμ˜ 슀트레슀λ₯Ό λœμ–΄μ€€λ‹€.

λŒ€λ¦¬μΈκ³Ό 본인을 λ‚˜λˆŒ ν•„μš”κ°€ μžˆμ„κΉŒ?

  • PrinterProxy ν΄λž˜μŠ€μ™€ Printer 클래슀λ₯Ό λ‚˜λˆ„μ§€ μ•Šκ³  Printer 클래슀 μ•ˆμ— μ²˜μŒλΆ€ν„° μ§€μ—° 평가 κΈ°λŠ₯을 λ„£μ–΄ λ‘˜ μˆ˜λ„ μžˆλ‹€.
  • κ·ΈλŸ¬λ‚˜ 뢄리λ₯Ό 톡해 ν”„λ‘œκ·Έλž¨μ΄ λΆ€ν’ˆν™”λ˜λ©΄ κ°œλ³„μ μœΌλ‘œ μˆ˜μ •ν•  수 μžˆλ‹€.
  • PrinterProxy 클래슀의 κ΅¬ν˜„μ„ λ°”κΎΈλ©΄ Printable μΈν„°νŽ˜μ΄μŠ€μ˜ 무엇을 λŒ€λ¦¬μΈμ΄ μ²˜λ¦¬ν•˜κ³  무엇을 μœ„μž„ν• μ§€ λ³€κ²½ ν•  수 μžˆλ‹€.
  • 그런 변경을 ν•˜λ”λΌλ„ Printer ν΄λž˜μŠ€λŠ” μˆ˜μ •ν•  ν•„μš”κ°€ μ „ν˜€ μ—†λ‹€.
  • μ§€μ—° 평가λ₯Ό μ „ν˜€ ν•˜μ§€ μ•ŠμœΌλ €λ©΄ Main ν΄λž˜μŠ€μ—μ„œ PrinterProxy ν΄λž˜μŠ€κ°€ μ•„λ‹Œ Printer 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό new ν‚€μ›Œλ“œλ‘œ μƒμ„±ν•˜λ©΄ λœλ‹€.
  • PrinterProxy ν΄λž˜μŠ€λŠ” Proxy μ—­μ΄λΌλŠ” κΈ°λŠ₯을 ν‘œν˜„ν•˜κ³  있기 λ•Œλ¬Έμ—, κ·Έ κΈ°λŠ₯을 μ‚¬μš©ν• μ§€ μ•ˆ ν• μ§€λŠ” PrinterProxyλ₯Ό μ‚¬μš©ν• μ§€ μ‚¬μš©ν•˜μ§€ μ•Šμ„μ§€λ‘œ κ²°μ •λœλ‹€.

λŒ€λ¦¬μ™€ μœ„μž„

  • λŒ€λ¦¬μΈ 혼자 μ²˜λ¦¬ν•  수 μžˆλŠ” 일은 직접 μ²˜λ¦¬ν•œλ‹€.
  • ν•˜μ§€λ§Œ κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ λ³ΈμΈμ—κ²Œ μœ„μž„ν•œλ‹€.
  • μ›λž˜ ν˜„μ‹€ μ„Έκ³„μ—μ„œλŠ” 본인이 λŒ€λ¦¬μΈμ—κ²Œ μ±…μž„μ„ λ§‘κΈ°λŠ”λ°, λ””μžμΈ νŒ¨ν„΄μ—μ„œλŠ” λ°˜λŒ€λ‘œ λ˜μ–΄ μžˆλ‹€.

νˆ¬κ³Όμ μ΄λž€?

  • Printer와 PrinterProxyλŠ” 같은 Printable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜κΈ° λ•Œλ¬Έμ—, Main ν΄λž˜μŠ€μ—μ„œ ν˜ΈμΆœν•˜λŠ” 곳이 μ–΄λ–€ ν΄λž˜μŠ€μ΄λ“  μƒκ΄€ν•˜μ§€ μ•ŠλŠ”λ‹€.
  • PrinterProxyλ₯Ό 쀑간에 두어도, Printerλ₯Ό 직접 μ΄μš©ν•΄λ„ 문제 없이 μ‚¬μš©ν•  수 μžˆλ‹€.
  • μ΄λŸ¬ν•œ νŠΉμ„±μ„ 투과적이라고 ν•œλ‹€.

HTTP ν”„λ‘μ‹œ

  • ν”„λ‘μ‹œλΌκ³  ν•˜λ©΄ HTTP ν”„λ‘μ‹œλ₯Ό λ– μ˜¬λ¦¬λŠ” μ‚¬λžŒμ΄ μžˆμ„ μˆ˜λ„ μžˆλ‹€.
  • HTTP ν”„λ‘μ‹œλŠ” HTTP μ„œλ²„μ™€ HTTP ν΄λΌμ΄μ–ΈνŠΈ μ‚¬μ΄μ—μ„œ μ›Ή νŽ˜μ΄μ§€ 캐싱 등을 ν•˜λŠ” μ†Œν”„νŠΈμ›¨μ–΄μ΄λ‹€.
  • 캐싱 λ˜ν•œ Proxy νŒ¨ν„΄μ„ μ μš©ν•΄μ„œ 생각할 수 μžˆλ‹€.
  • μ›Ή λΈŒλΌμš°μ €κ°€ μžˆλŠ” μ›Ή νŽ˜μ΄μ§€λ₯Ό ν‘œμ‹œν•  λ•Œ 원격지에 μžˆλŠ” μ›Ή μ„œλ²„μ— μ ‘μ†ν•΄μ„œ κ·Έ νŽ˜μ΄μ§€λ₯Ό κ°€μ Έμ˜€λŠ” 것이 μ•„λ‹ˆλΌ, HTTP ν”„λ‘μ‹œκ°€ 캐싱해 놓은 νŽ˜μ΄μ§€λ₯Ό λŒ€μ‹  κ°€μ Έμ˜¨λ‹€.
  • μ΅œμ‹  정보가 ν•„μš”ν•  λ•Œλ‚˜ μ›Ή νŽ˜μ΄μ§€μ˜ 유효 기간이 지났을 λ•Œ λΉ„λ‘œμ†Œ μ›Ή μ„œλ²„λ‘œ μ›Ή νŽ˜μ΄μ§€λ₯Ό κ°€μ§€λŸ¬ κ°„λ‹€.
  • μ—¬κΈ°μ„œλŠ” μ›Ή λΈŒλΌμš°μ €κ°€ Client, HTTP ν”„λ‘μ‹œκ°€ Proxy, 그리고 μ›Ή μ„œλ²„κ°€ RealSubject에 ν•΄λ‹Ήν•œλ‹€.

λ‹€μ–‘ν•œ Proxy

가상 ν”„λ‘μ‹œ(Virtual Proxy)
  • 이 μž₯μ—μ„œ μ†Œκ°œν•œ Proxy νŒ¨ν„΄
  • μ‹€μ œλ‘œ μΈμŠ€ν„΄μŠ€κ°€ ν•„μš”ν•œ μ‹œμ μ—μ„œ 생성 및 μ΄ˆκΈ°ν™”ν•œλ‹€.
원격 ν”„λ‘μ‹œ(Remote Proxy)
  • RealSubject 역이 λ„€νŠΈμ›Œν¬ μ €νŽΈμ— μžˆλ”λΌλ„ 마치 λ°”λ‘œ μ˜†μ— μžˆλŠ” κ²ƒμ²˜λŸΌ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  수 μžˆλ‹€.
  • Java RMI 등이 여기에 ν•΄λ‹Ήλœλ‹€.
보호 ν”„λ‘μ‹œ(Access Proxy)
  • Access ProxyλŠ” RealSubject μ—­μ˜ κΈ°λŠ₯에 λŒ€ν•΄ μ ‘κ·Ό μ œν•œμ„ μ„€μ •ν•œλ‹€.
  • μ§€μ •λœ μ‚¬μš©μžλΌλ©΄ λ©”μ„œλ“œ ν˜ΈμΆœμ„ ν—ˆκ°€ν•˜μ§€λ§Œ, λ‚˜λ¨Έμ§€λŠ” 였λ₯˜κ°€ λ˜λ„λ‘ μ²˜λ¦¬ν•˜λŠ” ν”„λ‘μ‹œμ΄λ‹€.
This post is licensed under CC BY 4.0 by the author.