앱 쪽 일을 하다가 웹 쪽으로 넘어갔다. 넘겨받은 코드를 열어 보니 화면을 그리는 파일 안에 DB 조회가 들어 있었다.
Table of contents
Open Table of contents
한 파일에 다 있었다
처음에는 아무 생각이 없었다. 오히려 편했다. JSP 하나만 열면 되고 값이 어디서 오는지도 바로 위 scriptlet 에 적혀 있다. 나중에 안 것인데 이 구조를 Model 1 이라고 부른다.
<%
Connection conn = DriverManager.getConnection(...);
PreparedStatement ps = conn.prepareStatement(
"SELECT id, name, point FROM member WHERE id = ?");
ps.setString(1, request.getParameter("id"));
ResultSet rs = ps.executeQuery();
%>
<table>
<% while (rs.next()) { %>
<tr><td><%= rs.getString("name") %></td></tr>
<% } %>
</table>
이 파일을 처음 열었을 때는 화면에 표를 그리는 코드로만 보였다.
증상 — 닫는 코드가 없었다
그 scriptlet 에 conn.close() 가 없었다. ResultSet 도 PreparedStatement 도 그대로였다.
한동안 문제가 안 났다. 접속이 적을 때는 커넥션이 남아도 티가 안 난다. 사람이 몰리는 시간에 화면이 멈춘다는 이야기가 나오고서야 봤다.
DriverManager.getConnection() 은 부를 때마다 새 커넥션을 연다. 안 닫으면 MySQL 쪽 max_connections 에 걸릴 때까지 쌓인다. SHOW PROCESSLIST 로 보니 Sleep 상태가 백 개가 넘었다.
finally 에서 닫게 고쳤다. 다만 JSP 안에서 try·finally 를 쓰니 HTML 과 섞여 읽기가 더 어려워졌다.
두 번째 화면과 복사
같은 회원 정보를 다른 화면에서도 보여 달라는 요청이 왔다. 마이페이지와 관리자 화면 두 곳이었다. 내가 한 일은 조회 부분을 그대로 복사해 새 파일에 붙이는 것이었다.
문제는 그다음이었다. 회원 테이블에 컬럼이 하나 늘었다. 그 값을 화면에 추가해야 하는데 같은 조회가 몇 군데 있는지 몰랐다. Eclipse 로 SELECT 를 찾아 다녔고, 세 곳을 고쳤다고 생각했는데 네 번째가 있었다. 그건 QA에서 나왔다.
화면 파일 안에 조회를 두면 안 된다는 말은 전에도 들었는데 왜 안 되는지는 몰랐다. 잘 돌아가고 있었기 때문이다. 이 일을 겪고 나서 이유가 손에 잡혔다. 같은 조회가 필요하면 복사하게 된다. 복사하고 나면 고칠 곳이 몇 개인지 알 방법이 전체 검색밖에 없다.
조회를 밖으로 뺐다
조회 코드를 MemberDao 라는 클래스로 옮겨 봤다. JSP 에는 그 클래스를 부르는 두 줄만 남았다.
<%
MemberDao dao = new MemberDao();
Member m = dao.findById(request.getParameter("id"));
%>
컬럼이 늘면 그 클래스 한 곳만 고치면 됐다. 화면 네 개는 그대로 두고. 이때 처음으로 고칠 곳이 하나가 되게 만든다는 감각이 생겼다. 설계라는 말을 이런 데 쓰는 것 같다.
얼마 뒤 프로젝트에 iBATIS 가 들어왔다. Java 안에 문자열로 쓰던 쿼리를 sqlmap XML 로 옮기는 것이다.
처음에는 왜 굳이 파일을 하나 더 만드나 싶었다. 자바 안에 있을 때가 더 가까워 보였다. 써 보니 쿼리가 여러 줄이어도 문자열을 이어 붙이지 않아도 됐다. 쿼리만 따로 보면서 고칠 수 있었다.
가장 컸던 것은 자바를 모르는 사람도 쿼리를 읽을 수 있다는 점이다. 성능 문제를 상의할 때 그 파일만 보내면 됐다.
정리하다 보니 아직 모르는 것이 하나 남았다. 요청에서 받은 값을 쿼리에 넣는 자리가 둘이었다. 어떤 자리는 # 를 쓰고 어떤 자리는 $ 를 썼다. 동작이 같아 보여서 넘어갔는데 굳이 두 방식이 있다면 이유가 있을 것이다. 다음에는 이것부터 알아봐야겠다.
정리
- 한 파일에 다 있으면 편하다. 그게 문제가 되기 전까지는
- 두 번째 화면이 생기는 순간 복사가 시작된다
- 복사하고 나면 고칠 곳이 몇 개인지 전체 검색 말고는 알 방법이 없다
- 조회를 밖으로 빼는 이유는 고칠 곳을 하나로 만들기 위해서다
- SQL을 별도 파일에 두면 쿼리만 따로 읽고 고칠 수 있다
- 자바를 모르는 사람도 그 파일은 읽는다. 성능 상의가 쉬워진다
- 같은 결과를 내는 두 방식이 있으면 왜 다른지 알아봐야 한다