๐ ํ๋ก์(Proxy) ํจํด - ์๋ฒฝ ๋ง์คํฐํ๊ธฐ
Proxy Pattern
ํ๋ก์ ํจํด(Proxy Pattern)์ ๋์ ์๋ณธ ๊ฐ์ฒด๋ฅผ ๋๋ฆฌํ์ฌ ๋์ ์ฒ๋ฆฌํ๊ฒ ํจ์ผ๋ก์จ ๋ก์ง์ ํ๋ฆ์ ์ ์ดํ๋ ํ๋ ํจํด์ด๋ค.
ํ๋ก์(Proxy)์ ์ฌ์ ์ ์ธ ์๋ฏธ๋ '๋๋ฆฌ์ธ'์ด๋ผ๋ ๋ป์ด๋ค. ์ฆ, ๋๊ตฐ๊ฐ์๊ฒ ์ด๋ค ์ผ์ ๋์ ์ํค๋ ๊ฒ์ ์๋ฏธํ๋๋ฐ, ์ด๋ฅผ ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ์ ๋ชฉํด๋ณด๋ฉด ํด๋ผ์ด์ธํธ๊ฐ ๋์ ๊ฐ์ฒด๋ฅผ ์ง์ ์ฐ๋๊ฒ ์๋๋ผ ์ค๊ฐ์ ํ๋ก์(๋๋ฆฌ์ธ)์ ๊ฑฐ์ณ์ ์ฐ๋ ์ฝ๋ ํจํด์ด๋ผ๊ณ ๋ณด๋ฉด ๋๋ค. ๋ฐ๋ผ์ ๋์ ๊ฐ์ฒด(Subject)์ ๋ฉ์๋๋ฅผ ์ง์ ์คํํ๋ ๊ฒ์ด ์๋, ๋์ ๊ฐ์ฒด์ ์ ๊ทผํ๊ธฐ ์ ์ ํ๋ก์(Proxy) ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ์ ๊ทผํ ํ ์ถ๊ฐ์ ์ธ ๋ก์ง์ ์ฒ๋ฆฌํ๋ค ์ ๊ทผํ๊ฒ ๋๋ค.
๊ทธ๋ฅ ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ฉด ๋์ง, ์ด๋ ๊ฒ ๋ฒ๊ฑฐ๋กญ๊ฒ ์ค๊ณ ๋๋ฆฌ์๋ฅผ ํตํด ์ด์ฉํ๋ ๋ฐฉ์์ ์ทจํ๋ ์ด์ ๋, ๋์ ํด๋์ค๊ฐ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๊ฑฐ๋ ์ธ์คํด์คํ ํ๊ธฐ์ ๋ฌด๊ฒ๊ฑฐ๋ ์ถ๊ฐ ๊ธฐ๋ฅ์ ๊ฐ๋ฏธํ๊ณ ์ถ์๋ฐ, ์๋ณธ ๊ฐ์ฒด๋ฅผ ์์ ํ ์ ์๋ ์ํฉ์ผ ๋๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด์ ์ด๋ค. ๋์ฒด์ ์ผ๋ก ์ ๋ฆฌํ์๋ฉด ๋ค์๊ณผ ๊ฐ์ ํจ๊ณผ๋ฅผ ๋๋ฆด์ ์๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
- ๋ณด์(Security) : ํ๋ก์๋ ํด๋ผ์ด์ธํธ๊ฐ ์์ ์ ์ํํ ์ ์๋ ๊ถํ์ด ์๋์ง ํ์ธํ๊ณ ๊ฒ์ฌ ๊ฒฐ๊ณผ๊ฐ ๊ธ์ ์ ์ธ ๊ฒฝ์ฐ์๋ง ์์ฒญ์ ๋์์ผ๋ก ์ ๋ฌํ๋ค.
- ์บ์ฑ(Caching) : ํ๋ก์๊ฐ ๋ด๋ถ ์บ์๋ฅผ ์ ์งํ์ฌ ๋ฐ์ดํฐ๊ฐ ์บ์์ ์์ง ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ์๋ง ๋์์์ ์์ ์ด ์คํ๋๋๋ก ํ๋ค.
- ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ(Data validation) : ํ๋ก์๊ฐ ์ ๋ ฅ์ ๋์์ผ๋ก ์ ๋ฌํ๊ธฐ ์ ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ๋ค.
- ์ง์ฐ ์ด๊ธฐํ(Lazy initialization) : ๋์์ ์์ฑ ๋น์ฉ์ด ๋น์ธ๋ค๋ฉด ํ๋ก์๋ ๊ทธ๊ฒ์ ํ์๋ก ํ ๋๊น์ง ์ฐ๊ธฐํ ์ ์๋ค.
- ๋ก๊น (Logging) : ํ๋ก์๋ ๋ฉ์๋ ํธ์ถ๊ณผ ์๋ ๋งค๊ฐ ๋ณ์๋ฅผ ์ธํฐ์ ํธํ๊ณ ์ด๋ฅผ ๊ธฐ๋กํ๋ค.
- ์๊ฒฉ ๊ฐ์ฒด(Remote objects) : ํ๋ก์๋ ์๊ฒฉ ์์น์ ์๋ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์์ ๋ก์ปฌ์ฒ๋ผ ๋ณด์ด๊ฒ ํ ์ ์๋ค.
ํ๋ก์ ํจํด ๊ตฌ์กฐ
ํ๋ก์๋ ๋ค๋ฅธ ๊ฐ์ฒด์ ๋ํ ์ ๊ทผ์ ์ ์ดํ๋ ๊ฐ์ฒด์ด๋ค. ์ฌ๊ธฐ์ ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋์(Subject)๋ผ๊ณ ๋ถ๋ฅธ๋ค. ํ๋ก์์ ๋์์ ๋์ผํ ์ธํฐํ์ด์ค๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ ์ด๋ฅผ ํตํด ๋ค๋ฅธ ์ธํฐํ์ด์ค์ ์์ ํ ํธํ๋๋๋ก ๋ฐ๊ฟ์ ์๋ค.
- Subject : Proxy์ RealSubject๋ฅผ ํ๋๋ก ๋ฌถ๋ ์ธํฐํ์ด์ค (๋คํ์ฑ)
- ๋์ ๊ฐ์ฒด์ ํ๋ก์ ์ญํ ์ ๋์ผํ๊ฒ ํ๋ ์ถ์ ๋ฉ์๋
operation()๋ฅผ ์ ์ํ๋ค. - ์ธํฐํ์ด์ค๊ฐ ์๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ๋ Proxy ์ญํ ๊ณผ RealSubject ์ญํ ์ ์ฐจ์ด๋ฅผ ์์ํ ํ์๊ฐ ์๋ค.
- ๋์ ๊ฐ์ฒด์ ํ๋ก์ ์ญํ ์ ๋์ผํ๊ฒ ํ๋ ์ถ์ ๋ฉ์๋
- RealSubject : ์๋ณธ ๋์ ๊ฐ์ฒด
- Proxy : ๋์ ๊ฐ์ฒด(RealSubject)๋ฅผ ์ค๊ณํ ๋๋ฆฌ์ ์ญํ
- ํ๋ก์๋ ๋์ ๊ฐ์ฒด๋ฅผ ํฉ์ฑ(composition)ํ๋ค.
- ํ๋ก์๋ ๋์ ๊ฐ์ฒด์ ๊ฐ์ ์ด๋ฆ์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉฐ, ๋ณ๋์ ๋ก์ง์ ์ํ ํ ์ ์๋ค (์ธํฐํ์ด์ค ๊ตฌํ ๋ฉ์๋)
- ํ๋ก์๋ ํ๋ฆ์ ์ด๋ง ํ ๋ฟ ๊ฒฐ๊ณผ๊ฐ์ ์กฐ์ํ๊ฑฐ๋ ๋ณ๊ฒฝ์ํค๋ฉด ์ ๋๋ค.
- Client : Subject ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํ์ฌ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํด ์ด์ฉ.
- ํด๋ผ์ด์ธํธ๋ ํ๋ก์๋ฅผ ์ค๊ฐ์ ๋๊ณ ํ๋ก์๋ฅผ ํตํด์ RealSubject์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋๋ค.
ํ๋ก์ ํจํด ์ข ๋ฅ
Proxy ํจํด์ ๋จ์ํ๋ฉด์๋ ์์ฃผ ์ฐ์ด๋ ํจํด์ด๋ฉฐ, ๊ทธ ํ์ฉ ๋ฐฉ์๋ ๋ค์ํ๋ค. ๊ฐ์ ํ๋ก์ ๊ฐ์ฒด๋ผ๋ ์ด๋ ํ ๋ก์ง์ ์ง๋๋์ ๋ฐ๋ผ ๊ทธ ํ์ฉ๋๋ ์ฒ์ฐจ๋ง๋ณ์ด ๋๋ค. Proxy ํจํด์ ๊ธฐ๋ณธํ์ ์ด๋ค ๋ฐฉ์์ผ๋ก ๋ณํํ๋๋์ ๋ฐ๋ผ ํ๋ก์ ์ข ๋ฅ๊ฐ ๋๋์ด์ง๊ฒ ๋๋ค.
๊ธฐ๋ณธํ ํ๋ก์ (Normal Proxy)
interface ISubject {
void action();
}
class RealSubject implements ISubject {
public void action() {
System.out.println("์๋ณธ ๊ฐ์ฒด ์ก์
!!");
}
}
class Proxy implements ISubject {
private RealSubject subject; // ๋์ ๊ฐ์ฒด๋ฅผ composition
Proxy(RealSubject subject) {
this.subject = subject;
}
public void action() {
subject.action(); // ์์
/* do something */
System.out.println("ํ๋ก์ ๊ฐ์ฒด ์ก์
!!");
}
}
class Client {
public static void main(String[] args) {
ISubject sub = new Proxy(new RealSubject());
sub.action();
}
}
๊ฐ์ ํ๋ก์ (Virtual Proxy)
- ์ง์ฐ ์ด๊ธฐํ ๋ฐฉ์
- ๊ฐ๋ ํ์ํ์ง๋ง ํญ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฌ๋์ด ์๋ ๋ฌด๊ฑฐ์ด ์๋น์ค ๊ฐ์ฒด๊ฐ ์๋ ๊ฒฝ์ฐ
- ์ด ๊ตฌํ์ ์ค์ ๊ฐ์ฒด์ ์์ฑ์ ๋ง์ ์์์ด ์๋ชจ ๋์ง๋ง ์ฌ์ฉ ๋น๋๋ ๋ฎ์ ๋ ์ฐ๋ ๋ฐฉ์์ด๋ค.
- ์๋น์ค๊ฐ ์์๋ ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋์ ์ ๊ฐ์ฒด ์ด๊ธฐํ๊ฐ ์ค์ ๋ก ํ์ํ ์์ ์ ์ด๊ธฐํ๋ ์ ์๋๋ก ์ง์ฐํ ์ ์๋ค.
class Proxy implements ISubject {
private RealSubject subject; // ๋์ ๊ฐ์ฒด๋ฅผ composition
Proxy() {
}
public void action() {
// ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ ์์ฒญ(action(๋ฉ์๋ ํธ์ถ)์ด ๋ค์ด ์์ ๋ ์ค์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
if(subject == null){
subject = new RealSubject();
}
subject.action(); // ์์
/* do something */
System.out.println("ํ๋ก์ ๊ฐ์ฒด ์ก์
!!");
}
}
class Client {
public static void main(String[] args) {
ISubject sub = new Proxy();
sub.action();
}
}
๋ณดํธ ํ๋ก์ (Protection Proxy)
- ํ๋ก์๊ฐ ๋์ ๊ฐ์ฒด์ ๋ํ ์์์ผ๋ก์ ์์ธ์ค ์ ์ด(์ ๊ทผ ๊ถํ)
- ํน์ ํด๋ผ์ด์ธํธ๋ง ์๋น์ค ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ํ๋ ๊ฒฝ์ฐ
- ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํตํด ํด๋ผ์ด์ธํธ์ ์๊ฒฉ ์ฆ๋ช ์ด ๊ธฐ์ค๊ณผ ์ผ์นํ๋ ๊ฒฝ์ฐ์๋ง ์๋น์ค ๊ฐ์ฒด์ ์์ฒญ์ ์ ๋ฌํ ์ ์๊ฒ ํ๋ค.
class Proxy implements ISubject {
private RealSubject subject; // ๋์ ๊ฐ์ฒด๋ฅผ composition
boolean access; // ์ ๊ทผ ๊ถํ
Proxy(RealSubject subject, boolean access) {
this.subject = subject;
this.access = access;
}
public void action() {
if(access) {
subject.action(); // ์์
/* do something */
System.out.println("ํ๋ก์ ๊ฐ์ฒด ์ก์
!!");
}
}
}
class Client {
public static void main(String[] args) {
ISubject sub = new Proxy(new RealSubject(), false);
sub.action();
}
}
๋ก๊น ํ๋ก์ (Logging Proxy)
- ๋์ ๊ฐ์ฒด์ ๋ํ ๋ก๊น ์ ์ถ๊ฐํ๋ ค๋ ๊ฒฝ์ฐ
- ํ๋ก์๋ ์๋น์ค ๋ฉ์๋๋ฅผ ์คํํ๊ธฐ ์ ๋ฌํ๊ธฐ ์ ์ ๋ก๊น ์ ํ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํ์ฌ ์ฌ์ ์ํ๋ค.
class Proxy implements ISubject {
private RealSubject subject; // ๋์ ๊ฐ์ฒด๋ฅผ composition
Proxy(RealSubject subject {
this.subject = subject;
}
public void action() {
System.out.println("๋ก๊น
..................");
subject.action(); // ์์
/* do something */
System.out.println("ํ๋ก์ ๊ฐ์ฒด ์ก์
!!");
System.out.println("๋ก๊น
..................");
}
}
class Client {
public static void main(String[] args) {
ISubject sub = new Proxy(new RealSubject());
sub.action();
}
}
์๊ฒฉ ํ๋ก์ (Remote Proxy)
- ํ๋ก์ ํด๋์ค๋ ๋ก์ปฌ์ ์๊ณ , ๋์ ๊ฐ์ฒด๋ ์๊ฒฉ ์๋ฒ์ ์กด์ฌํ๋ ๊ฒฝ์ฐ
- ํ๋ก์ ๊ฐ์ฒด๋ ๋คํธ์ํฌ๋ฅผ ํตํด ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ์ ๋ฌํ์ฌ ๋คํธ์ํฌ์ ๊ด๋ จ๋ ๋ถํ์ํ ์์ ๋ค์ ์ฒ๋ฆฌํ๊ณ ๊ฒฐ๊ณผ๊ฐ๋ง ๋ฐํ
- ํด๋ผ์ด์ธํธ ์ ์ฅ์์ ํ๋ก์๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ ๊ฒ์ด๋ ์๊ฒฉ์ด๋ ๋ก์ปฌ์ด๋ ์ ๊ฒฝ ์ธ ํ์๊ฐ ์์ผ๋ฉฐ, ํ๋ก์๋ ์ง์ง ๊ฐ์ฒด์ ํต์ ์ ๋๋ฆฌํ๊ฒ ๋๋ค.
์ฐธ๊ณ ๋ก ํ๋ก์๋ฅผ ์คํฐ๋ธ๋ผ๊ณ ๋ ๋ถ๋ฅด๋ฉฐ, ํ๋ก์๋ก๋ถํฐ ์ ๋ฌ๋ ๋ช ๋ น์ ์ดํดํ๊ณ ์ ํฉํ ๋ฉ์๋๋ฅผ ํธ์ถํด์ฃผ๋ ์ญํ ์ ํ๋ ๋ณด์กฐ๊ฐ์ฒด๋ฅผ ์ค์ผ๋ ํค์ด๋ผ ํ๋ค.
์บ์ฑ ํ๋ก์ (Caching Proxy)
- ๋ฐ์ดํฐ๊ฐ ํฐ ๊ฒฝ์ฐ ์บ์ฑํ์ฌ ์ฌ์ฌ์ฉ์ ์ ๋
- ํด๋ผ์ด์ธํธ ์์ฒญ์ ๊ฒฐ๊ณผ๋ฅผ ์บ์ํ๊ณ ์ด ์บ์์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌ
[ HTTP Proxy ]
HTTP Proxy๋ ์น์๋ฒ์ ๋ธ๋ผ์ฐ์ ์ฌ์ด์์ ์น ํ์ด์ง์ ์บ์ฑ์ ์คํํ๋ ์ํํธ์จ์ด์ด๋ค. ์น ๋ธ๋ผ์ฐ์ ๊ฐ ์ด๋ค ์น ํ์ด์ง๋ฅผ ํ์ํ ๋ ์ง์ ์น ์๋ฒ์์ ๊ทธ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ด ์๋๊ณ , HTTP Proxy๊ฐ ์บ์ฌํด์ ์ด๋ค ํ์ด์ง๋ฅผ ๋์ ํด์ ์ทจ๋ํ๋ค. ๋ง์ผ ์ต์ ์ ๋ณด๊ฐ ํ์ํ๊ฑฐ๋ ํ์ด์ง์ ์ ํจ๊ธฐ๊ฐ์ด ์ง๋ฌ์ ๋ ์น ์๋ฒ์ ์น ํ์ด์ง๋ฅผ ๊ฐ์ง๋ฌ ๊ฐ๋ค.
์ด๋ฅผ ํจํด์ผ๋ก ๋ฐ์ ธ๋ณด๋ฉด, ์น ๋ธ๋ผ์ฐ์ ๊ฐ Client ์ญํ , HTTP Proxy๊ฐ Proxy ์ญํ , ๊ทธ๋ฆฌ๊ณ ์น ์๋ฒ๊ฐ RealSubcjet ์ญํ ์ ํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
ํ๋ก์ ํจํด ํน์ง
ํจํด ์ฌ์ฉ ์๊ธฐ
- ์ ๊ทผ์ ์ ์ดํ๊ฑฐ๊ฐ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ณ ์ถ์๋ฐ, ๊ธฐ์กด์ ํน์ ๊ฐ์ฒด๋ฅผ ์์ ํ ์ ์๋ ์ํฉ์ผ๋
- ์ด๊ธฐํ ์ง์ฐ, ์ ๊ทผ ์ ์ด, ๋ก๊น , ์บ์ฑ ๋ฑ, ๊ธฐ์กด ๊ฐ์ฒด ๋์์ ์์ ์์ด ๊ฐ๋ฏธํ๊ณ ์ถ์ ๋
ํจํด ์ฅ์
- ๊ฐ๋ฐฉ ํ์ ์์น(OCP) ์ค์
- ๊ธฐ์กด ๋์ ๊ฐ์ฒด์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ ์๋ค.
- ๋จ์ผ ์ฑ
์ ์์น(SRP) ์ค์
- ๋์ ๊ฐ์ฒด๋ ์์ ์ ๊ธฐ๋ฅ์๋ง ์ง์ค ํ๊ณ , ๊ทธ ์ด์ธ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์ญํ ์ ํ๋ก์ ๊ฐ์ฒด์ ์์ํ์ฌ ๋ค์ค ์ฑ ์์ ํํผ ํ ์ ์๋ค.
- ์๋ ํ๋ ค๋ ๊ธฐ๋ฅ์ ์ํํ๋ฉฐ ๊ทธ์ธ์ ๋ถ๊ฐ์ ์ธ ์์ (๋ก๊น , ์ธ์ฆ, ๋คํธ์ํฌ ํต์ ๋ฑ)์ ์ํํ๋๋ฐ ์ ์ฉํ๋ค
- ํด๋ผ์ด์ธํธ๋ ๊ฐ์ฒด๋ฅผ ์ ๊ฒฝ์ฐ์ง ์๊ณ , ์๋น์ค ๊ฐ์ฒด๋ฅผ ์ ์ดํ๊ฑฐ๋ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค.
- ์ฌ์ฉ์ ์ ์ฅ์์๋ ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ ๊ฐ์ฒด๋ ์ฌ์ฉ๋ฒ์ ์ ์ฌํ๋ฏ๋ก ์ฌ์ฉ์ฑ์ ๋ฌธ์ ๋์ง ์๋๋ค.
ํจํด ๋จ์
- ๋ง์ ํ๋ก์ ํด๋์ค๋ฅผ ๋์
ํด์ผ ํ๋ฏ๋ก ์ฝ๋์ ๋ณต์ก๋๊ฐ ์ฆ๊ฐํ๋ค.
- ์๋ฅผ๋ค์ด ์ฌ๋ฌ ํด๋์ค์ ๋ก๊น ๊ธฐ๋ฅ์ ๊ฐ๋ฏธ ์ํค๊ณ ์ถ๋ค๋ฉด, ๋์ผํ ์ฝ๋๋ฅผ ์ ์ฉํจ์๋ ๊ฐ๊ฐ์ ํด๋์ค์ ํด๋น๋๋ ํ๋ก์ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ์ ์ฉํด์ผ ๋๊ธฐ ๋๋ฌธ์ ์ฝ๋๋์ด ๋ง์์ง๊ณ ์ค๋ณต์ด ๋ฐ์ ๋๋ค.
- ์๋ฐ์์๋ ๋ฆฌํ๋ ์ ์์ ์ ๊ณตํ๋ ๋์ ํ๋ก์(Dynamic Proxy) ๊ธฐ๋ฒ์ ์ด์ฉํด์ ํด๊ฒฐํ ์ ์๋ค. (ํ์ )
- ํ๋ก์ ํด๋์ค ์์ฒด์ ๋ค์ด๊ฐ๋ ์์์ด ๋ง๋ค๋ฉด ์๋น์ค๋ก๋ถํฐ์ ์๋ต์ด ๋ฆ์ด์ง ์ ์๋ค.
์์ ๋ฅผ ํตํด ์์๋ณด๋ Proxy ํจํด
๊ฐ์ ํ๋ก์ ํจํด ๊ตฌํํ๊ธฐ (์์ ๊ด๋ฆฌ)
์ด๋ฏธ์ง ๋ทฐ์ด ํ๋ก๊ทธ๋จ์ ๋ง๋ ๋ค๊ณ ๊ฐ์ ํด๋ณด์. ์ด๋ฏธ์ง ๋ทฐ์ด๋ ๊ณ ํด์๋์ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ค๋ค.
๊ณ ํด์๋ ์ด๋ฏธ์ง ๊ฒฝ๋ก๋ฅผ ์ธ์๋ก ๋ฐ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฌํ๊ณ , showImage() ๋ฉ์๋๊ฐ ํธ์ถํ๋ฉด ํ๋ฉด์ ๋ ๋๋ง ํ๋ HighREsolutionImage ํด๋์ค๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑํด ์ฃผ์๋ค.
class HighResolutionImage {
String img;
HighResolutionImage(String path) {
loadImage(path);
}
private void loadImage(String path) {
// ์ด๋ฏธ์ง๋ฅผ ๋์คํฌ์์ ๋ถ๋ฌ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฌ (์์
์์ฒด๊ฐ ๋ฌด๊ฒ๊ณ ๋ง์ ์์์ ํ์๋กํจ)
try {
Thread.sleep(1000);
img = path;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s์ ์๋ ์ด๋ฏธ์ง ๋ก๋ฉ ์๋ฃ\n", path);
}
@Override
public void showImage() {
// ์ด๋ฏธ์ง๋ฅผ ํ๋ฉด์ ๋ ๋๋ง
System.out.printf("%s ์ด๋ฏธ์ง ์ถ๋ ฅ\n", img);
}
}
ํด๋ฆฐํ์ง ์์ ๋ฌธ์ ์ ์ฝ๋ โ
HighREsolutionImage ํด๋์ค๋ฅผ ์ด๋ฏธ์ง ๋ทฐ์ด์ธ ImageViewr ํด๋์ค์์ ์ด๋ฏธ์ง 3๊ฐ๋ฅผ ๋ฑ๋กํ๊ณ ๋ค์๊ณผ ๊ฐ์ด ๋ก๋ํ๋ค๊ณ ํ๋ค.
class ImageViewer {
public static void main(String[] args) {
HighResolutionImage highResolutionImage1 = new HighResolutionImage("./img/๊ณ ํด์๋์ด๋ฏธ์ง_1");
HighResolutionImage highResolutionImage2 = new HighResolutionImage("./img/๊ณ ํด์๋์ด๋ฏธ์ง_2");
HighResolutionImage highResolutionImage3 = new HighResolutionImage("./img/๊ณ ํด์๋์ด๋ฏธ์ง_3");
highResolutionImage2.showImage();
}
}
ํ์ง๋ง ์คํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์์ ์๋ฏ์ด, ๋๋ฒ์งธ ๊ณ ํด์๋ ์ด๋ฏธ์ง๋ฅผ showImage ํ๋๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ด ๋ง์ด ๋๋ฆฌ๋๊ฑธ ๋ณผ ์ ์๋ค. ์๋ํ๋ฉด ์ด๋ฏธ์ง๋ฅผ ์ค๋นํ๋ ๊ณผ์ ์์ ์๊ฐ์ ๋ค ๋บ์ด๋จน์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฐ๋ฐ ๊ฐ๋ง ์๊ฐํด๋ณด๋ฉด ๋ง์ผ ์ฌ์ฉ์๊ฐ ๋ชฉ๋ก์์ ์ด๋ฏธ์ง๋ฅผ ์ ํํ๊ธฐ ์ ๊น์ง ๊ตณ์ด ์ด๋ฏธ์ง๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ค๋น์ํฌ ํ์๊ฐ ์์ ๊ฒ์ด๋ค. ์ฌ์ฉ์๊ฐ ๋ชฉ๋ก์์ ์ ํํ ์ด๋ฏธ์ง๋ง ๋ก๋ฉ์ํค๋ฉด ๋์ง ์์๊น?
ํ๋ก์ ํจํด์ ์ ์ฉํ ์ฝ๋ โ๏ธ
์ฆ, ํ๋ก์ ํด๋์ค์์ ์ฌ์ฉ์๊ฐ ์ ํํ ์ด๋ฏธ์ง๋ง ๋ก๋ํด์ ๋ ๋๋งํ๋๋ก ๋์ ๊ฐ์ฒด๋ฅผ ์ ์ด๋ฅผ ํ๋ฉด ๋๋ ๊ฒ์ด๋ค. ๊ฐ์ ํ๋ก์๋ฅผ ์ด์ฉํด ๋ฆ์ ์ด๊ธฐํ(Lazy Initialisation)๋ก ์ค์ ๊ฐ์ฒด์ ์ฌ์ฉ ์์ ์ ์ ์ดํด๋ณด์. ์ฐธ๊ณ ๋ก HighResolutionImage ํด๋์ค๊ฐ ๋์ ๊ฐ์ฒด(RealSubject) ๊ฐ ๋๋ค.
ํ๋ก์๋ฅผ ๊ตฌํํ๋๋ฐ ์์ด ๊ฐ์ฅ ๋จผ์ ํด์ผํ ์ผ์, ๋์ ๊ฐ์ฒด์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํ๋๋ก ๋ฌถ์ด์ฃผ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ๋ ๊ฒ์ด๋ค. ๋คํ์ฑ์ ์ด์ฉํด ๋์, ํ๋ก์ ๊ฐ์ฒด ๋ชจ๋๋ฅผ ํ๋์ ํ์ ์ผ๋ก ๋ค๋ฃจ๊ธฐ ์ํด์๋ค.
// ๋์ ๊ฐ์ฒด์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ฌถ๋ ์ธํฐํ์ด์ค (๋คํ์ฑ)
interface IImage {
void showImage(); // ์ด๋ฏธ์ง๋ฅผ ๋ ๋๋งํ๊ธฐ ์ํด ๊ตฌํ์ฒด๊ฐ ๊ตฌํํด์ผ ํ๋ ์ถ์๋ฉ์๋
}
// ๋์ ๊ฐ์ฒด (RealSubject)
class HighResolutionImage implements IImage {
String img;
HighResolutionImage(String path) {
loadImage(path);
}
private void loadImage(String path) {
// ์ด๋ฏธ์ง๋ฅผ ๋์คํฌ์์ ๋ถ๋ฌ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฌ (์์
์์ฒด๊ฐ ๋ฌด๊ฒ๊ณ ๋ง์ ์์์ ํ์๋กํจ)
try {
Thread.sleep(1000);
img = path;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s์ ์๋ ์ด๋ฏธ์ง ๋ก๋ฉ ์๋ฃ\n", path);
}
@Override
public void showImage() {
// ์ด๋ฏธ์ง๋ฅผ ํ๋ฉด์ ๋ ๋๋ง
System.out.printf("%s ์ด๋ฏธ์ง ์ถ๋ ฅ\n", img);
}
}
// ํ๋ก์ ๊ฐ์ฒด (Proxy)
class ImageProxy implements IImage {
private IImage proxyImage;
private String path;
ImageProxy(String path) {
this.path = path;
}
@Override
public void showImage() {
// ๊ณ ํด์๋ ์ด๋ฏธ์ง ๋ก๋ฉํ๊ธฐ
proxyImage = new HighResolutionImage(path);
proxyImage.showImage();
}
}
class ImageViewer {
public static void main(String[] args) {
IImage highResolutionImage1 = new ImageProxy("./img/๊ณ ํด์๋์ด๋ฏธ์ง_1");
IImage highResolutionImage2 = new ImageProxy("./img/๊ณ ํด์๋์ด๋ฏธ์ง_2");
IImage highResolutionImage3 = new ImageProxy("./img/๊ณ ํด์๋์ด๋ฏธ์ง_3");
highResolutionImage2.showImage();
}
}
์ด๋ฏธ์ง ๋ทฐ์ด ํด๋์ค(ํด๋ผ์ด์ธํธ)์ ์ฝ๋ ์์ฒด๋ ํฌ๊ฒ ๋ณํ์ง ์์๋ค. ๋๊ฐ์ด ์ด๋ฏธ์ง 3๊ฐ๋ฅผ ๋ก๋ํ๋ ์ฝ๋์ธ๋ฐ, ๋์ ๊ฐ์ฒด ๋์ ์ ํ๋ก์ ๊ฐ์ฒด์ ํ ๋นํ ์ ์ด ๋ค๋ฅผ ๋ฟ์ด๋ค. ํ์ง๋ง ๋ด๋ถ ๋์ ๋ฐฉ์์ ํฌ๊ฒ ๋ณ๊ฒฝ๋์๋๋ฐ, ํ๋ก์ ๊ฐ์ฒด ๋ด์์ ๊ฒฝ๋ก ๋ฐ์ดํฐ๋ฅผ ์ง๋๊ณ ์๋ค๊ฐ ์ฌ์ฉ์๊ฐ showImage๋ฅผ ํธ์ถํ๋ฉด ๊ทธ๋์์ผ ๋์ ๊ฐ์ฒด๋ฅผ ๋ก๋(lazyload)ํ์ฌ, ์ด๋ฏธ์ง๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฌํ๊ณ ๋์ ๊ฐ์ฒด์ showIMage() ๋ฉ์๋๋ฅผ ์์ ํธ์ถํจ์ผ๋ก์จ, ์ค์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ์์ ์ ๋ฉ๋ชจ๋ฆฌ ์ ์ฌ๊ฐ ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์ ๋ถํ์ํ ์์๋ญ๋น๊ฐ ๋ฐ์ํ์ง ์๊ฒ ๋์๋ค.
๋ณดํธ ํ๋ก์ ํจํด ๊ตฌํํ๊ธฐ
์ด๋ ํ์ฌ์์ ์ง์๋ค์ด ๊ฐ ํ์ฌ ๊ตฌ์ฑ์๋ค์ ์ ๋ณด๋ค์ ์๋ฌด ์ ์ฝ์์ด ๋ชจ๋ ์ด๋ํ ์ ์์๋ค๊ณ ํ๋ค. ๊ทธ๋์ ์ธ์ฌํ์์ ๋ณด์์ ์ํด ์ธ์ฌ ์ ๋ณด์ ๋ํ ๋ฐ์ดํฐ ์ ๊ทผ์ ์ง์ฑ ๋จ์๋ก ์ธ๋ถํ ํ๋ ค๊ณ ํ๋ค. ์๋ฅผ๋ค์ด ์ฌ์์ ์ค๋ก์ง ์์ ์ง์ฑ ๊ณผ ๊ฐ์ ์ฌ์๋ค ์ ๋ณด๋ง ์ด๋ํ ์ ์์ผ๋ฉฐ ๊ทธ ์์ ๊ณผ์ฅ์ด๋ ์๋ฌด ์ ๋ณด๋ ์ด๋ํ ์ ์๋ ์์ด๋ค. ๊ทธ๋์ ๊ธฐ์กด์ ํ๋ก๊ทธ๋จ์ ๋ก์ง์ ์ ๋ฐ์ดํธํ ํ์๊ฐ ์๋๋ฐ, ๊ทธ๋ฐ๋ฐ ๊ธฐ์กด์ ํ๋ก๊ทธ๋จ์ ์์ ํ๊ธฐ์๋ ๋๋ฌด๋ ๋ฐฉ๋ํ๊ณ ๋ณต์กํด์ ๋๊ด์ ๋ถ๋ชํ๋ค๊ณ ํ๋ค. ์ด๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ ๊น?
๊ธฐ์กด ํ๋ก๊ทธ๋จ์ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑ๋์ด ์๋ค๊ณ ํ๋ค. ๋จผ์ ํ์ฌ์ ์ง์ฑ ์ ํํํ๋ RESPONSIBILITY enum ์์ ํด๋์ค๋ฅผ ์ ์ํ์๋ค. ์ฐจ๋ก๋๋ก ์ฌ์(Staff), ๊ณผ์ฅ(Manager), ์๋ฌด(Director) ๋ฅผ ํํํ๋ ์์๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
// ์ง์ฑ
์์
enum RESPONSIBILITY {
STAFF, // ์ฌ์
MANAGER, // ๊ณผ์ฅ
DIRECTOR // ์๋ฌด
}
๊ทธ๋ฆฌ๊ณ ์ง์์ ์ ๋ณด๋ฅผ ํด๋์คํํ Employee ํด๋์ค๋ฅผ ์ ์ํ์๋ค. ์ด ํด๋์ค์๋ ๊ตฌ์ฑ์์ ์ด๋ฆ๊ณผ ์ง์ฑ
์ ์ ๋ณด๋ฅผ ํ๋ฒ์ ์์ ์๋ getInfo() ๋ฉ์๋๋ฅผ ์ง์ํ๋ค.
// ๊ตฌ์ฑ์ ํด๋์ค
class Employee {
private String name; // ์ด๋ฆ
private RESPONSIBILITY position; // ์ง์
public Employee(String name, RESPONSIBILITY position) {
this.name = name;
this.position = position;
}
public String getName() {
return name;
}
public RESPONSIBILITY getGrade() {
return position;
}
public String getInfo(Employee viewer) {
return "Display " + getGrade().name() + " '" + getName() + "' personnel information.";
}
}
๋ง์ง๋ง์ผ๋ก ํ์ฌ์ ์ ๊ตฌ์ฑ์์ ๋ชจ๋ ์ถ๋ ฅํ๋ PrintEmployeeInfo ํด๋์ค ํ๋ก๊ทธ๋จ์ด ์๋ค. ์ด ํด๋์ค๋ ์์ฑ์์ ์ธ์๋ก ๋๊ฐ ์กฐํํ๋์ง ๋์์(viewer)๋ฅผ ๋ฐ๊ณ ๋ชจ๋ ๊ตฌ์ฑ์ ๋ฆฌ์คํธ๋ฅผ ์ธ์๋ก ๋ฐ์ ์ถ๋ ฅํด์ฃผ๋ printAllInfo() ๋ฉ์๋๋ฅผ ์ง์ํ๋ค.
class PrintEmployeeInfo {
Employee viewer; // ์กฐํํ๋ ค๋ ์
PrintEmployeeInfo(Employee viewer) {
this.viewer = viewer;
}
// Employee ๊ฐ์ฒด ๋ฆฌ์คํธ๋ฅผ ๋ฐ์ ์ง์๋ค์ ์ ๋ณด๋ฅผ ์ํํ์ฌ ์กฐํ
void printAllInfo(List<Employee> employees) {
employees.stream()
.map(employee -> employee.getInfo(viewer))
.forEach(System.out::println);
}
}
ํด๋ฆฐํ์ง ์์ ๋ฌธ์ ์ ์ฝ๋ โ
์ง์ฑ
์ ๋ํ ํด๋์ค ๊ตฌ์ฑ ์์ฒด๋ ํฐ ๋ฌธ์ ๊ฐ ์๋ค. ๊ทธ๋ฌ๋ ์ง๊ธ ์ด ์ํ๋ก๋ง ๋๋๋ค๋ฉด ์ด๋ ๋๊ตฌ๋ ์ง PrintEmployeeInfo ๊ฐ์ฒด๋ฅผ ํตํด printAllInfo() ๋ฉ์๋๋ฅผ ์คํ์์ผ ๋ชจ๋ ์ง์์ ๋ฆฌ์คํธ๋ฅผ ๋ณผ์ ์๊ฒ ๋๋ค.
public static void main(String[] args) {
// ์ง์๋ณ ๊ฐ์ธ ๊ฐ์ฒด ์์ฑ
Employee CTO = new Employee("Dragon Jung", RESPONSIBILITY.DIRECTOR);
Employee devManager = new Employee("Cats Chang", RESPONSIBILITY.MANAGER);
Employee financeManager = new Employee("Dell Choi", RESPONSIBILITY.MANAGER);
Employee devStaff = new Employee("Dark Kim", RESPONSIBILITY.STAFF);
Employee financeStaff = new Employee("Pal Yoo", RESPONSIBILITY.STAFF);
// ์ง์๋ค์ ๋ฆฌ์คํธ๋ก ๊ฐ๊ณต
List<Employee> employees = Arrays.asList(CTO, devManager, financeManager, devStaff, financeStaff);
/*-----------------------------------------------------------------------------------------*/
// ๋ : ์ผ๊ฐ ์ฌ์ ์ง์ฑ
Employee me = new Employee("inpa", RESPONSIBILITY.STAFF);
System.out.println("\n================================================================");
System.out.println("์๋๋ฆฌ์ค1. ์ผ๊ฐ ์ฌ์์ธ ๋ด๊ฐ ํ์ฌ ์ธ์ ์ธ์ฌ ์ ๋ณด ์กฐํ");
System.out.println("================================================================");
PrintEmployeeInfo view = new PrintEmployeeInfo(me); // ๋ชจ๋ ์ง์ ์ ๋ณด๋ฅผ ์ถ๋ ฅํ๋ ํด๋์ค
view.printAllInfo(employees); // ์ผ๊ฐ ์ฌ์์ ๋ถ๊ตฌํ๊ณ ๋ชจ๋ ์ง์ ์กฐํ
}
๋ฐ๋ผ์ ์ง์์ ๋ฐ๋ผ ์ ๋ณด ์ด๋ ์ ๊ทผ ์ ํ์ ๋์ด์ผ ๋๋๋ฐ, ๊ธฐ์กด์ ํ๋ก๊ทธ๋จ์ ์์ ํ๊ธฐ์ ๋น์ฉ์ด ๋ง์ด ๋ ๋ค๊ณ ํ๋ค. ์ด๋ด๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํตํด ๊ธฐ์กด์ ํ๋ก๊ทธ๋จ์ ์ผ๋ถ ๊ธฐ๋ฅ์ ์ ์ดํ๋๋ก ํ๋ฉด ๋๋ค.
ํ๋ก์ ํจํด์ ์ ์ฉํ ์ฝ๋ โ๏ธ
๋ณดํธ ํ๋ก์๋ ํ๋ก์ ๊ฐ์ฒด๊ฐ ์ฌ์ฉ์์ ์ค์ ๊ฐ์ฒด์ ๋ํ ์ ๊ทผ์ ์ ์ดํ๋ค. ์ฌ๊ธฐ์ ์ง์ฑ ์ ๋ฐ๋ฅธ ์ ๋ณด ์ด๋ ์ ๊ทผ ์ ์ด์ด๋ค.
์ฌ๋ฌ๋ฒ ๋งํ๋ฏ์ด ํ๋ก์๋ฅผ ๊ตฌ์ฑํ๊ธฐ์ ์์, ์ฐ์ ๋์ ๊ฐ์ฒด์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ชจ๋ ๋ฌถ์ด์ฃผ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ์ธํด์ค๋ค.
// ๊ตฌ์ฑ์ ์ธํฐํ์ด์ค
interface IEmployee {
String getName(); // ๊ตฌ์ฑ์์ ์ด๋ฆ
RESPONSIBILITY getGrade(); // ๊ตฌ์ฑ์์ ์ง์ฑ
String getInfo(IEmployee viewer); // ๊ตฌ์ฑ์์ ์ธ์ฌ์ ๋ณด
}
๊ทธ๋ฆฌ๊ณ ์ด๋๊น์ง Employee ํด๋์ค ํ์ ์ผ๋ก ๋ฐ์ ๋ชจ๋ ๋ณ์์ ๋งค๊ฐ๋ณ์์ ํ์ ์ ์ธํฐํ์ด์ค๋ก ์ฌ์ค์ ํด์ค๋ค.
์ด์ ๋ณธ๊ฒฉ์ ์ธ ํ๋ก์ ํด๋์ค๋ฅผ ์ค์ ํ ์ฐจ๋ก์ด๋ค. ๊ทธ๋์ ๋ฌด์ง์ฑ์ผ๋ก ๋ชจ๋ ๊ตฌ์ฑ์์ ์ถ๋ ฅํด์ฃผ๋ ๋์ ๊ฐ์ฒด์ getInfo() ๋ฉ์๋๋ฅผ ์ ์ด ๋ก์ง์ ์ถ๊ฐํ๊ณ ๋์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ์์ ํธ์ถํด์ค์ผ๋ก์จ ๋ณดํธ ํ๋ก์๋ฅผ ๊ตฌ์ฑํ ์ ์๊ฒ ๋๋ค.
// ๋ณดํธ ํ๋ก์ : ์ธ์ฌ์ ๋ณด๊ฐ ๋ณดํธ๋ ๊ตฌ์ฑ์ (์ธ์ฌ ์ ๋ณด ์ด๋ ๊ถํ ์์ผ๋ฉด ์์ธ ๋ฐ์)
class ProtectedEmployee implements IEmployee {
private IEmployee employee;
public ProtectedEmployee(IEmployee employee) {
this.employee = employee;
}
@Override
public String getInfo(IEmployee viewer) {
RESPONSIBILITY position = this.employee.getGrade(); // ์กฐํ ๋นํ๋ ์์ ์ง์ฑ
์ ์ป๊ธฐ
// ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ์กฐํ์์ ์ง์ฑ
์ ๋ฐ๋ผ ์ถ๋ ฅ์ ์ ์ด
switch (viewer.getGrade()) {
case DIRECTOR:
// ๋ถ์ฌ์ฅ์ ๊ณผ์ฅ, ์ฌ์๋ค์ ๋ณผ ์ ์๋ค.
return this.employee.getInfo(viewer);
case MANAGER:
// ๊ณผ์ฅ์ ๊ฐ์ ์ง๋ฌด์ ๊ทธ ์๋ ์ฌ์๋ค์ ๋ณผ ์ ์๋ค. ์ฌ์ฅ ์ ๋ณด๋ ๋ณผ ์ ์๋ค.
if (position != RESPONSIBILITY.DIRECTOR) {
return this.employee.getInfo(viewer);
}
case STAFF:
// ์ฌ์์ ๊ฐ์ ์ง๋ฌด์ธ ์ฌ๋๋ค๋ง ๋ณผ ์ ์๋ค. ๊ณผ์ฅ, ์ฌ์ฅ ์ ๋ณด๋ ๋ณผ ์ ์๋ค.
if (position != RESPONSIBILITY.DIRECTOR && position != RESPONSIBILITY.MANAGER) {
return this.employee.getInfo(viewer);
}
default: return "๋ค๋ฅธ ์ฌ๋์ ์ธ์ฌ์ ๋ณด๋ฅผ ์กฐํ ํ ์ ์์ต๋๋ค";
}
}
@Override
public String getName() {
return employee.getName();
}
@Override
public RESPONSIBILITY getGrade() {
return employee.getGrade();
}
}
์ด์ ๊ธฐ์กด ํ๋ก๊ทธ๋จ์ ๋ฑ๋ก๋์ด ์๋ employees ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์ ํ๋ก์ ๊ฐ์ฒด๋ก ๊ฐ์ธ์(wrap) ํ๋ก์๋ก ์ธ์คํด์คํ ํด์ค๋ค.
๊ทธ๋ฆฌ๊ณ ํ๋ก์๋ก ์ฌ๊ตฌ์ฑ๋ ๋ฆฌ์คํธ List<IEmployee> protectedEmployees๋ฅผ PrintEmployeeInfo ๊ฐ์ฒด์ printAllInfo() ๋ฉ์๋ ์ธ์๋ก ๋๊ฒจ์ฃผ๊ฒ๋๋ฉด, getInfo() ํ๋ ๊ณผ์ ์์ ํ๋ก์์ switch ๋ก์ง์ ๊ฑธ๋ฆฌ๊ฒ ๋์ด ์กฐํ์์ ๊ถํ์ด ์๋ค๋ฉด ๊ฒฝ๊ณ ๋ฌธ๊ตฌ๋ฅผ ๋ณด๋ด์ฃผ๊ฒ ๋๋ค.
class Client {
public static void main(String[] args) {
// ์ง์๋ณ ๊ฐ์ธ ๊ฐ์ฒด ์์ฑ
Employee CTO = new Employee("Dragon Jung", RESPONSIBILITY.DIRECTOR);
Employee devManager = new Employee("Cats Chang", RESPONSIBILITY.MANAGER);
Employee financeManager = new Employee("Dell Choi", RESPONSIBILITY.MANAGER);
Employee devStaff = new Employee("Dark Kim", RESPONSIBILITY.STAFF);
Employee financeStaff = new Employee("Pal Yoo", RESPONSIBILITY.STAFF);
// ์ง์๋ค์ ๋ฆฌ์คํธ๋ก ๊ฐ๊ณต
List<Employee> employees = Arrays.asList(CTO, devManager, financeManager, devStaff, financeStaff);
/*-----------------------------------------------------------------------------------------*/
// ๊ธฐ์กด์ ๋ฑ๋ก๋ ๋ฆฌ์คํธ๋ฅผ ์์ ํ ์ ์์ผ๋, ๋์ ์ผ๋ก ๊ธฐ์กด์ Employee ๊ฐ์ฒด๋ฅผ ํ๋ก์ ๊ฐ์ฒด ProtectedEmployee๋ก Wrapํ๋ ์์
์ ์คํํ๋ค.
List<IEmployee> protectedEmployees = new ArrayList<>();
for (Employee e : employees) {
protectedEmployees.add(new ProtectedEmployee((IEmployee) e));
}
/*-----------------------------------------------------------------------------------------*/
// ๋ : ์ผ๊ฐ ์ฌ์ ์ง์ฑ
Employee me = new Employee("inpa", RESPONSIBILITY.STAFF);
System.out.println("\n================================================================");
System.out.println("์๋๋ฆฌ์ค1. ์ผ๊ฐ ์ฌ์์ธ ๋ด๊ฐ ํ์ฌ ์ธ์ ์ธ์ฌ ์ ๋ณด ์กฐํ");
System.out.println("================================================================");
PrintEmployeeInfo view = new PrintEmployeeInfo(me);
view.printAllInfo(protectedEmployees);
System.out.println("\n================================================================");
System.out.println("์๋๋ฆฌ์ค2. ๊ณผ์ฅ์ด ํ์ฌ ์ธ์ ์ธ์ฌ ์ ๋ณด ์กฐํ");
System.out.println("================================================================");
PrintEmployeeInfo view2 = new PrintEmployeeInfo(devManager);
view2.printAllInfo(protectedEmployees);
System.out.println("\n================================================================");
System.out.println("์๋๋ฆฌ์ค3. ์๋ฌด๊ฐ ํ์ฌ ์ธ์ ์ธ์ฌ ์ ๋ณด ์กฐํ");
System.out.println("================================================================");
PrintEmployeeInfo view3 = new PrintEmployeeInfo(CTO);
view3.printAllInfo(protectedEmployees);
}
}
์ค๋ฌด์์ ์ฐพ์๋ณด๋ Proxy ํจํด
Java
- java.lang.reflect.Proxy
- java.rmi.* (์๊ฒฉ ํ๋ก์ ๋ชจ๋)
- javax.ejb.EJB
- javax.inject.Inject
- javax.persistence.PersistenceContext
Dynamic Proxy
๊ฐ๋ฐ์๊ฐ ์ง์ ๋์์ธ ํจํด์ผ๋ก์ ํ๋ก์ ํจํด์ ๊ตฌํํด๋ ๋์ง๋ง, ์๋ฐ JDK์์๋ ๋ณ๋๋ก ํ๋ก์ ๊ฐ์ฒด ๊ตฌํ ๊ธฐ๋ฅ์ ์ง์ํ๋ค. ์ด๋ฅผ ๋์ ํ๋ก์(Dynamic Proxy) ๊ธฐ๋ฒ์ด๋ผ๊ณ ๋ถ๋ฆฌ์ด๋ค.
๋์ ํ๋ก์๋ ๊ฐ๋ฐ์๊ฐ ์ง์ ์ผ์ผํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์๋, ์ ํ๋ฆฌ์ผ์ด์ ์คํ ๋์ค java.lang.reflect.Proxy ํจํค์ง์์ ์ ๊ณตํด์ฃผ๋ API๋ฅผ ์ด์ฉํ์ฌ ๋์ ์ผ๋ก ํ๋ก์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ์ผ๋ก์, ์๋ฐ์ Reflection API ๊ธฐ๋ฒ์ ์์ฉํ ์ฐ์ฅ์ ์ ๊ฐ๋ ์ด๋ค. ๊ทธ๋์ ๋ณ๋์ ํ๋ก์ ํด๋์ค ์ ์์์ด ๋ฐํ์์ผ๋ก ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋์ ์ผ๋ก ์์ฑํด ์ด์ฉํ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค.
// ๋์ ๊ฐ์ฒด์ ํ๋ก์๋ฅผ ๋ฌถ๋ ์ธํฐํ์ด์ค
interface Animal {
void eat();
}
// ํ๋ก์๋ฅผ ์ ์ฉํ ํ๊ฒ ๊ฐ์ฒด
class Tiger implements Animal{
@Override
public void eat() {
System.out.println("ํธ๋์ด๊ฐ ์์์ ๋จน์ต๋๋ค.");
}
}
public class Client {
public static void main(String[] arguments) {
// newProxyInstance() ๋ฉ์๋๋ก ๋์ ์ผ๋ก ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๋ค.
Animal tigerProxy = (Animal) Proxy.newProxyInstance(
Animal.class.getClassLoader(), // ๋์ ๊ฐ์ฒด์ ์ธํฐํ์ด์ค์ ํด๋์ค๋ก๋
new Class[]{Animal.class}, // ๋์ ๊ฐ์ฒด์ ์ธํฐํ์ด์ค
new InvocationHandler() { // ํ๋ก์ ํธ๋ค๋ฌ
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object target = new Tiger();
System.out.println("----eat ๋ฉ์๋ ํธ์ถ ์ ----");
Object result = method.invoke(target, args); // ํ๊ฒ ๋ฉ์๋ ํธ์ถ
System.out.println("----eat ๋ฉ์๋ ํธ์ถ ํ----");
return result;
}
}
);
tigerProxy.eat();
}
}
Spring Framework
์คํ๋ง AOP
์คํ๋ง ํ๋ ์์ํฌ์์๋ ๋ด๋ถ์ ์ผ๋ก ํ๋ก์ ๊ธฐ์ ์ ์ ๋ง ๋ง์ด ์ฌ์ฉํ๊ณ ์๋ค. (AOP, JPA ๋ฑ)
์คํ๋ง์์๋ Bean์ ๋ฑ๋กํ ๋ Singleton์ ์ ์งํ๊ธฐ ์ํด Dynamic Proxy ๊ธฐ๋ฒ์ ์ด์ฉํด ํ๋ก์ ๊ฐ์ฒด๋ฅผ Bean์ผ๋ก ๋ฑ๋กํ๋ค. ๋ํ Bean์ผ๋ก ๋ฑ๋กํ๋ ค๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ์ฒด๊ฐ Interface๋ฅผ ํ๋๋ผ๋ ๊ตฌํํ๊ณ ์์ผ๋ฉด JDK๋ฅผ ์ด์ฉํ๊ณ Interface๋ฅผ ๊ตฌํํ๊ณ ์์ง ์์ผ๋ฉด ๋ด์ฅ๋ CGLIB ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ๋ค.
@Service
public class GameService {
public void startDame() {
System.out.println("์ด ์๋ฆฌ์ ์ค์ ์ฌ๋ฌ๋ถ์ ์ง์ฌ์ผ๋ก ํ์ํฉ๋๋ค.");
}
}
@Aspect
@Comonent
public class PerfAspect {
@Around("bean(gameService)")
public void timestamp(ProceedingJoinPoint point) throws Throwable {
System.out.println("ํ๋ก์ ์คํ 1");
point.proceed(); // ๋์ ๊ฐ์ฒด์ ์๋ณธ ๋ฉ์๋๋ฅผ ์คํ
System.out.println("ํ๋ก์ ์คํ 2");
}
}
JavaScript
Proxy / Reflect ๊ฐ์ฒด
์๋ฐ์์๋ Proxy ๊ฐ์ฒด๋ฅผ ๋ณ๋๋ก ์ง์ํ๋ฏ์ด, ์๋ฐ์คํฌ๋ฆฝํธ ์ง์์์๋ ๋ ๋ฆฝ์ ์ธ Proxy ๊ฐ์ฒด๊ฐ ์กด์ฌํ๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์์์ Proxy ๊ฐ์ฒด์ ์ญํ ์ ๋์ ๊ฐ์ฒด์ ๊ฐ์ธ์(wrapping), ์์ฑ ์กฐํ, ํ ๋น, ์ด๊ฑฐ ๋ฐ ํจ์ ํธ์ถ ๋ฑ ์ฌ๋ฌ ๊ธฐ๋ณธ ๋์์ ๊ฐ๋ก์ฑ(trap) ํน๋ณํ ๋ค๋ฅธ ๋์์ ๊ฐ๋ฏธ์ํค๋ ๋๋ฆฌ์ ์ญํ ์ ํ๋ค. ๋์ ๊ฐ์ฒด๋ Object, Array ๋ฑ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋ชจ๋ ์๋ฃํ์ด ๋์์ด ๋ ์ ์๋ค.
let obj = {
name: 'ํ๊ธธ๋',
print: function () {
console.log(`My Name is ${this.name}`);
},
};
// print ํจ์๋ฅผ ํ๋ก์๋ก ๊ฐ์ธ๊ธฐ
obj.print = new Proxy(obj.print, {
apply(target, thisArg, args) {
console.log('๋ฉ์๋ ์คํํ ๋ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ด ๋ก์ง ์ํ');
console.log('target: ', target); // ๋์ ํจ์
console.log('thisArg: ', thisArg); // this์ ๊ฐ
console.log('args: ', args); // ๋งค๊ฐ๋ณ์ ๋ชฉ๋ก (๋ฐฐ์ด)
console.log('์ด๋ฆ ๋ฐ๊ฟ ๋ฒ๋ฆฌ๊ธฐ ~');
thisArg.name = '์๊บฝ์ ';
Reflect.apply(target, thisArg, args); // ๋์ ์๋ณธ ํจ์ ์คํ
},
});
obj.print();
# ์ฐธ๊ณ ์๋ฃ
์ฝ๋ฉ์ผ๋ก ํ์ตํ๋ GoF์ ๋์์ธ ํจํด - ๋ฐฑ๊ธฐ์
https://refactoring.guru/design-patterns/proxy
https://effectiveprogramming.tistory.com/entry/Proxy-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EA%B7%B8-%ED%99%9C%EC%9A%A9
https://www.oodesign.com/proxy-pattern
https://jdm.kr/blog/235