** ๐Ÿ“Œ PART 8.1 Memory Safe MVC ๊ตฌ์กฐ๋กœ ๊ฐœ์„ ํ•˜๊ธฐ **

โ€œ๋ฉ”๋ชจ๋ฆฌ๋„ ์•ˆ์ „ํ•˜๊ณ , ์œ ์ง€๋ณด์ˆ˜๋„ ์‰ฌ์šด ๊ตฌ์กฐ๋กœ ์ฝ”๋“œ๋ฅผ ์ •๋ฆฌํ•ด๋ณด์ž!โ€


๐Ÿ”ง ํ•ต์‹ฌ ๋ชฉํ‘œ:


๐ŸŸข 1๋‹จ๊ณ„. MVC2 ๊ตฌ์กฐ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์œ„ํ—˜ ์ง€์ ์€ ์–ด๋””?

๐Ÿ“ฆ ๊ธฐ๋ณธ ๊ตฌ์กฐ

[Client] โ†’ Controller โ†’ Service โ†’ DAO โ†’ DB
                       โ†“
                    View(JSP)

โ— ๋ฉ”๋ชจ๋ฆฌ ์œ„ํ—˜ ํฌ์ธํŠธ

๋ ˆ์ด์–ด ์ž˜๋ชป๋œ ์‚ฌ์šฉ ๋ฌธ์ œ์ 
Controller ๋งค ์š”์ฒญ๋งˆ๋‹ค ๊ฐ์ฒด ์ƒ์„ฑ GC ๋Œ€์ƒ ๋งŽ์•„์ง
Service ์ƒํƒœ ์žˆ๋Š” ๊ฐ์ฒด ๊ณต์œ  Thread-safe ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
DAO DB ์—ฐ๊ฒฐ์„ ์ง์ ‘ ๊ด€๋ฆฌ Connection ๋ˆ„์ˆ˜, ๋ฉ”๋ชจ๋ฆฌ ๊ณผ๋‹ค ์‚ฌ์šฉ

โœ… ๋ฆฌํŒฉํ† ๋ง ์ „๋žต 1: Controller / Service / DAO ๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „ ์„ค๊ณ„


โœ… [1] Controller: ์ƒํƒœ ์—†๋Š” ์‹ฑ๊ธ€ํ†ค + ๋ฉ”์†Œ๋“œ ๋‹จ์œ„ ์ฒ˜๋ฆฌ

@WebServlet("/member/list")
public class MemberController extends HttpServlet {
    private MemberService service = new MemberService(); // โœ… ์ƒํƒœ ์—†์Œ!

    protected void doGet(HttpServletRequest req, HttpServletResponse res) {
        List<Member> list = service.getAll();
        req.setAttribute("list", list);
        req.getRequestDispatcher("memberList.jsp").forward(req, res);
    }
}

๐Ÿ’ก ํ•ต์‹ฌ


โœ… [2] Service: ์ƒํƒœ ์—†๋Š” ๊ฐ์ฒด, ์š”์ฒญ๋งˆ๋‹ค ๋™์ž‘๋งŒ ์ˆ˜ํ–‰

public class MemberService {
    private MemberDao dao = MemberDao.getInstance(); // โœ… DAO๋Š” ์‹ฑ๊ธ€ํ†ค

    public List<Member> getAll() {
        return dao.selectAll();
    }
}

๐Ÿ’ก ํ•ต์‹ฌ


โœ… [3] DAO: ์‹ฑ๊ธ€ํ†ค + ์ปค๋„ฅ์…˜ ํ’€ ์‚ฌ์šฉ + ์ƒํƒœ ์—†์Œ

public class MemberDao {
    private static final MemberDao instance = new MemberDao();
    private MemberDao() {} // ์ƒ์„ฑ์ž private
    public static MemberDao getInstance() {
        return instance;
    }

    public List<Member> selectAll() {
        try (Connection conn = DBConnection.getConnection();
             PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM member")) {
            ...
        }
    }
}

๐Ÿ’ก ํ•ต์‹ฌ


โœ… ๋ฆฌํŒฉํ† ๋ง ์ „๋žต 2: ServletContextListener๋กœ ์ž์› ๋กœ๋”ฉ ์ตœ์ ํ™”


โ“ ServletContextListener๋ž€?

๐Ÿ’ฌ ํ†ฐ์บฃ ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ข…๋ฃŒ๋  ๋•Œ,

๊ณตํ†ต ์ž์›(DB, ์„ค์ •๊ฐ’, DAO ๋“ฑ) ์„ ๋ฏธ๋ฆฌ ๋กœ๋”ฉํ•˜๊ฑฐ๋‚˜ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ


โœ… ๊ตฌ์กฐ

@WebListener
public class AppInitListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("โœ… ์„œ๋ฒ„ ์‹œ์ž‘: ๊ณตํ†ต ์ž์› ๋กœ๋”ฉ ์ค‘...");
        ServletContext ctx = sce.getServletContext();
        ctx.setAttribute("appName", "JSPํฌํ„ธ");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("๐Ÿงน ์„œ๋ฒ„ ์ข…๋ฃŒ: ์ž์› ์ •๋ฆฌ ์ค‘...");
    }
}

๐Ÿ“ฆ ํ™œ์šฉ ์˜ˆ์‹œ


โœ… ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™” ํฌ์ธํŠธ

์ „๋žต ์„ค๋ช…
์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ ๊ณต์šฉ ๊ฐ์ฒด 1๋ฒˆ๋งŒ ์ƒ์„ฑ ๋งค ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ ๋งŒ๋“ค์ง€ ์•Š์•„ ๋ฉ”๋ชจ๋ฆฌ ์ ˆ์•ฝ
ํ•„์š” ์—†์„ ๋• context.removeAttribute() ์ˆ˜๋ช… ์กฐ์ ˆ ๊ฐ€๋Šฅ (GC ๋Œ€์ƒ ์œ ๋„)
์„œ๋ฒ„ ์ข…๋ฃŒ ์‹œ ์—ฐ๊ฒฐ ํ•ด์ œ (contextDestroyed) ํŒŒ์ผ ํ•ธ๋“ค, ๋กœ๊ทธ ๋“ฑ ์ž์› ๋ˆ„์ˆ˜ ๋ฐฉ์ง€ ๊ฐ€๋Šฅ

๐ŸŽ“ ๋ฉด์ ‘ ํฌ์ธํŠธ ์ •๋ฆฌ

์งˆ๋ฌธ ๋ชจ๋ฒ” ๋‹ต๋ณ€
Controller๋Š” ์™œ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋ฉด ์•ˆ ๋˜๋‚˜์š”? ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๋ฏ€๋กœ, ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜์— ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฉด ์ถฉ๋Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค
DAO๋Š” ์™œ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ์„ค๊ณ„ํ•˜๋‚˜์š”? ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด์ด๋ฉฐ, DB ์—ฐ๊ฒฐ์„ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค
ServletContextListener๋Š” ์™œ ์“ฐ๋‚˜์š”? ์„œ๋ฒ„ ์‹œ์ž‘/์ข…๋ฃŒ ์‹œ์ ์— ํ•„์š”ํ•œ ์ž์›(DB, ์„ค์ • ๋“ฑ)์„ ๋กœ๋”ฉํ•˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ ˆ์•ฝํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค
GC๊ฐ€ Controller ๊ตฌ์กฐ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋‚˜์š”? ์ƒํƒœ๊ฐ€ ์žˆ๋Š” Controller๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๊ธฐ ๋•Œ๋ฌธ์— ์ƒํƒœ ์—†๋Š” ๊ตฌ์กฐ๊ฐ€ GC์— ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค

โœ… ๋งˆ๋ฌด๋ฆฌ ์š”์•ฝํ‘œ

ํ•ญ๋ชฉ ๊ฐœ์„  ์ „๋žต ๋ฉ”๋ชจ๋ฆฌ ํšจ๊ณผ
Controller ์ƒํƒœ ์—†๋Š” ๊ตฌ์กฐ Thread-safe, GC ์ˆ˜๊ฑฐ ์‰ฌ์›€
Service Stateless ๊ตฌ์กฐ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ, ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์—†์Œ
DAO ์‹ฑ๊ธ€ํ†ค + ์ปค๋„ฅ์…˜ํ’€ + try-with-resources ๋ฆฌ์†Œ์Šค ์•ˆ์ •์„ฑ ์ตœ๊ณ 
ServletContextListener ์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ ์ž์› 1ํšŒ ๋กœ๋”ฉ ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„ โ†“, ์ดˆ๊ธฐํ™” ๋น„์šฉ โ†“

โœ… PART 8.2 Spring MVC vs JSP ๋ชจ๋ธ2 ๊ตฌ์กฐ ๋น„๊ต + ํ•œ๊ณ„

โ€œ์™œ JSP Model2์—์„œ Spring MVC๋กœ ๋„˜์–ด๊ฐ€์•ผ ํ• ๊นŒ?โ€


๐ŸŸข 1๋‹จ๊ณ„. ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ ์‹œ์ž‘ โ€“ Model2๋ž€?

๐Ÿ’ฌ JSP Model2๋Š” โ€œ์„œ๋ธ”๋ฆฟ์ด ์ปจํŠธ๋กค๋Ÿฌ ์—ญํ• โ€์„ ํ•˜๊ณ ,

JSP๋Š” ๋ทฐ(view) ์—ญํ• ๋งŒ ๋งก๋Š” ๊ตฌ์กฐ์•ผ.

[์‚ฌ์šฉ์ž ์š”์ฒญ] โ†’ Controller(Servlet) โ†’ Service โ†’ DAO โ†’ JSP(View)

๐Ÿ“Œ ์ด ๊ตฌ์กฐ๋งŒ์œผ๋กœ๋„ MVC ํŒจํ„ด์€ ๋˜์ง€๋งŒ, Spring์ฒ˜๋Ÿผ ์ž๋™ํ™”, ์„ค์ • ์ฃผ์ž…์€ ์—†์Œ!


๐Ÿ” Spring MVC๋Š” ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅผ๊นŒ?


โœ… Spring MVC ๊ตฌ์กฐ (IoC + DI + AOP)

[DispatcherServlet] โ†’ [Controller] โ†’ [Service] โ†’ [DAO]
                     โ†˜               โ†—
                      [IoC Container] (๊ฐ์ฒด ์ž๋™ ๊ด€๋ฆฌ)

โš™๏ธ 2๋‹จ๊ณ„. Spring์˜ IoC / DI / AOP ๊ตฌ์กฐ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ๋ฏธ์น˜๋Š” ๊ธ์ •์  ์˜ํ–ฅ


โœ… IoC (์ œ์–ด์˜ ์—ญ์ „)๋ž€?

๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์ง€ ์•Š๊ณ , Spring์ด ๋Œ€์‹  ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋ฐฉ์‹

// Model2 ๋ฐฉ์‹
MemberService service = new MemberService();  // ์ง์ ‘ new โŒ

// Spring ๋ฐฉ์‹
@Autowired
MemberService service;  // โœจ ์ž๋™ ์ฃผ์ž… (DI)

โœ… DI (์˜์กด์„ฑ ์ฃผ์ž…)์œผ๋กœ ์–ป๋Š” ์ด์ 

ํ•ญ๋ชฉ ์„ค๋ช… ๋ฉ”๋ชจ๋ฆฌ ํšจ๊ณผ
์žฌ์‚ฌ์šฉ์„ฑ โ†‘ ํ•˜๋‚˜ ๋งŒ๋“  ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์— ์ฃผ์ž… ์ค‘๋ณต ๊ฐ์ฒด ์ƒ์„ฑ โ†“
ํ…Œ์ŠคํŠธ ์‰ฌ์›€ Mock ๊ฐ์ฒด๋„ ์‰ฝ๊ฒŒ ์ฃผ์ž… ๊ฐ€๋Šฅ ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ โ†“
GC ์œ ๋ฆฌ ๊ฐ์ฒด ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ IoC๊ฐ€ ๊ด€๋ฆฌ โ†’ ํ•„์š” ์—†์œผ๋ฉด GC๋กœ ์ž์—ฐ ์ •๋ฆฌ๋จ โœ… ย 

โœ… AOP (๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ)

๋กœ๊ทธ ์ถœ๋ ฅ, ํŠธ๋žœ์žญ์…˜, ๋ณด์•ˆ ์ฒ˜๋ฆฌ ๊ฐ™์€ ๊ฑธ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ฐ”๊นฅ์—์„œ ์ž๋™ ์ฒ˜๋ฆฌ

// AOP ๋ฏธ์‚ฌ์šฉ ์‹œ
log.info("์‹œ์ž‘");
memberService.register(member);
log.info("์ข…๋ฃŒ");

// AOP ์‚ฌ์šฉ ์‹œ
@Loggable
public void register(Member member) {
    ...
}

๐Ÿง  ํ•ต์‹ฌ:


๐Ÿšซ 3๋‹จ๊ณ„. JSP Model2 ๊ตฌ์กฐ์—์„œ DI ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ณ‘๋ชฉ ์ง€์ 


โ— ๋ฌธ์ œ 1: ์ง์ ‘ new ์ƒ์„ฑ

public class BoardController extends HttpServlet {
    private BoardService service = new BoardService(); // โŒ ํ•ญ์ƒ new
}
๋ฌธ์ œ์  ์„ค๋ช…
๊ฐ์ฒด ์žฌ์‚ฌ์šฉ ๋ถˆ๊ฐ€ ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ฉด GC ๋ถ€๋‹ด โ†‘
ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋„ฃ์„ ์ˆ˜ ์—†์Œ (Mock ๋ถˆ๊ฐ€)
์ˆœํ™˜ ์˜์กด ๋ฐฉ์ง€ ๋ถˆ๊ฐ€ ์˜์กด์„ฑ ๊ด€๊ณ„๊ฐ€ ๊ผฌ์ด๋ฉด ํ•ด๊ฒฐ ์–ด๋ ค์›€

โ— ๋ฌธ์ œ 2: ์ƒํƒœ ์žˆ๋Š” Controller ์„ค๊ณ„ ์œ„ํ—˜

@WebServlet("/board/write")
public class WriteController extends HttpServlet {
    private List<String> tempList = new ArrayList<>(); // โŒ ๊ณต์œ ๋จ
}

โ— ๋ฌธ์ œ 3: ๊ณตํ†ต ์ฒ˜๋ฆฌ(AOP ๋ถˆ๊ฐ€)

๊ธฐ๋Šฅ JSP Model2์—์„œ ๋ถˆํŽธํ•œ ์ด์œ 
๋กœ๊ทธ์ธ ์ฒดํฌ ์ปจํŠธ๋กค๋Ÿฌ๋งˆ๋‹ค ์ฝ”๋“œ ์‚ฝ์ž…ํ•ด์•ผ ํ•จ
ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ ๋ชจ๋“  DAO์— try-catch ๋ฐ˜๋ณต
๋กœ๊น… ์ค‘๋ณต ์ฝ”๋“œ ๋ฐœ์ƒ, ์„ฑ๋Šฅ ์ €ํ•˜

โ†’ Spring์—์„œ๋Š” AOP๋กœ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ โ†’ ์„ฑ๋Šฅ + ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”


๐ŸŽ“ 4๋‹จ๊ณ„. ๋ฉด์ ‘ ํฌ์ธํŠธ ์ •๋ฆฌ

์งˆ๋ฌธ ๋ชจ๋ฒ” ๋‹ต๋ณ€ ์š”์•ฝ
JSP Model2์—์„œ DI๊ฐ€ ์–ด๋ ต๋‹ค๋Š” ๊ฑด ๋ฌด์Šจ ๋œป์ธ๊ฐ€์š”? ๊ฐ์ฒด๋ฅผ new๋กœ ์ง์ ‘ ๋งŒ๋“ค๊ธฐ ๋•Œ๋ฌธ์— IoC ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์—†์–ด์„œ ์˜์กด์„ฑ ์ฃผ์ž…์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
Spring์ด ๋ฉ”๋ชจ๋ฆฌ์ ์œผ๋กœ ๋” ์œ ๋ฆฌํ•œ ์ด์œ ๋Š”? IoC ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•ด ๊ณต์œ ํ•˜๊ณ , ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์„ ์ค„์—ฌ GC ํšจ์œจ์ด ๋†’์Šต๋‹ˆ๋‹ค
JSP Model2๋Š” ์–ด๋–ค ๊ตฌ์กฐ์  ํ•œ๊ณ„๊ฐ€ ์žˆ๋‚˜์š”? ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ ๋ถ€์กฑ, ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€, ๊ณตํ†ต ์ฒ˜๋ฆฌ ์–ด๋ ค์›€, GC ๋ถ€๋‹ด ์ฆ๊ฐ€ ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค
AOP๊ฐ€ ์™œ ์„ฑ๋Šฅ์— ์ข‹์€๊ฐ€์š”? ๋กœ๊ทธ, ๋ณด์•ˆ, ํŠธ๋žœ์žญ์…˜ ๊ฐ™์€ ๋น„ํ•ต์‹ฌ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•ด ์ฝ”๋“œ๋Ÿ‰๊ณผ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€๋‹ด์„ ์ค„์—ฌ์ค๋‹ˆ๋‹ค

โœ… ๋งˆ๋ฌด๋ฆฌ ์š”์•ฝํ‘œ

ํ•ญ๋ชฉ JSP Model2 Spring MVC
๊ฐ์ฒด ์ƒ์„ฑ ์ง์ ‘ new IoC๊ฐ€ ์ž๋™ ์ƒ์„ฑ
DI(์˜์กด์„ฑ ์ฃผ์ž…) ๋ถˆ๊ฐ€ ์ž๋™ ์ฃผ์ž… (@Autowired)
AOP ์ ์šฉ ์ˆ˜๋™, ๋ณต์žก ๊ฐ„๋‹จํžˆ ์ ์šฉ (๋กœ๊ทธ, ๋ณด์•ˆ, ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ)
๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์ง์ ‘ ๊ด€๋ฆฌ โ†’ ๋ˆ„์ˆ˜ ์œ„ํ—˜ Spring์ด ์ƒ๋ช…์ฃผ๊ธฐ ์ž๋™ ๊ด€๋ฆฌ โ†’ GC ํšจ์œจ โ†‘
ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ ๋‚ฎ์Œ ๋†’์Œ (Mock ์ฃผ์ž… ๊ฐ€๋Šฅ)