CRUD를 활용하여 회원가입을 진행하고, 고객의 정보가 데이터베이스에 저장한 후 이를 통해 로그인을 할 수 있는 방법을 배우게 됐다. 먼저 회원가입 창은 기존 CRUD를 진행함에 있어 다른점이 없기 때문에 새롭게 배운 부분만 살펴보려고 한다.

 

	public Member getUser(String id, String pw) {
		
		Member mb = null;

		Connection conn = DBconn();
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		try {
			
			String sql = "select * from member where id=? and password=?";
			ps = conn.prepareStatement(sql);
			ps.setString(1, id);
			ps.setString(2, pw);
			
			rs = ps.executeQuery();
			
			if(rs.next()) {
				System.out.println("일치하는 회원 발견!");
				
				mb = new Member();
				mb.setId(rs.getString("id"));
				mb.setPassword(rs.getString("password"));
				mb.setName(rs.getString("name"));
				mb.setGender(rs.getString("gender"));
				mb.setBirth(rs.getString("birth"));
				mb.setMail(rs.getString("mail"));
				mb.setPhone(rs.getString("phone"));
				mb.setAddress(rs.getString("address"));
				mb.setRegist_day(rs.getTimestamp("regist_day"));
				
				
			} else {
				System.out.println("아이디 혹은 패스워드가 다릅니다.");
			}
			
		} catch(SQLException e) { System.out.println("회원 조회 실패"); }
		
		finally {
			try {
				
				if(rs != null) { rs.close(); }
				if(ps != null) { ps.close(); }
				if(conn != null) { conn.close(); }
				
			} catch (Exception e2) { System.out.println("SQL리소스 닫기 실패"); }
		}
        
		return mb;
	}

 

   먼저 로그인을 진행하게 되면 회원의 정보를 데이터베이스에서 가져오기 전에 아이디와 비밀번호를 조회하여 모두 맞다면 반환하고 그렇지 않다면 다르다는 경고문을 띄워야 한다. 먼저 회원의 정보를 담을 객체 Member를 생성해야 하는데, 초기에 null값을 주는 이유는 컨트롤러에 넘어가서 해당 객체의 null값으로 true/false를 판단해야 하기 때문이다. if문에서 ResultSet으로 받은 데이터가 있는지 조회하고, 없으면 아무것도 담지 못한 채 컨트롤러에 null을 반환할 수 있도록 말이다.

 

   SQL문도 조금은 달라졌는데, where명령어에 and를 추가하면 두 개 이상의 조건이 맞는 데이터만 반환해준다. 조건에 맞는 데이터가 있다면 해당 데이터를 반환할 것이고, 없다면 -1(false)을 반환하기 때문에 if문이 실행되지 않는다.

 

@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException {

		String id = req.getParameter("id");
		String password = req.getParameter("password");

		MemberRepository dao = MemberRepository.getRepository();
		Member dto = dao.getUser(id, password);
		
		if(dto != null) {
			//로그인에 성공했으므로 세션 생성
			HttpSession session = req.getSession(true);
			session.setAttribute("member", dto);
			RequestDispatcher rd = req.getRequestDispatcher("resultMember.jsp?msg=2");
			rd.forward(req, resp);
		
		} else { resp.sendRedirect("member_login?error=1"); }
		
		
	}

 

   먼저 로그인 화면에서 받아온 ID와 PW를 위에서 작성했던 함수에 넣어 결과값을 받아 다시 컨트롤러로 돌아오게 된다. 여기서 반환받은 Member객체가 null(주소값도 존재하지 않는 상태)이 아니라면 데이터를 받아왔다는 뜻이 되므로 로그인 유지에 필요한 세션을 생성해준다. 이때 주의해야할 점이 있는데, 지금 다루고 있는 페이지가 jsp페이지라는 사실이다. 해당 페이지는 세션을 자동으로 생성해주기 때문에, 로그인의 세션을 받아와서 동작을 제한하기 위해서는 이 속성을 모두 꺼줘야한다. 심지어 jsp는 페이지마다 세션이 모두 true로 되어 있어서 모든 페이지를 하나하나 다… 꺼줘야한다. 한 페이지라도 true로 되어 있다면 해당 기능은 제대로 동작하지 않을 것이니 유의하자.

 

   여기서 로그인을 하게 될 때, 실패할 때 모두 어떤 파라미터를 들고가게 되는데 해당 값에 따라 보여지는 페이지가 다르게 나오도록 설정해두었다. 성공하면 환영 인사 페이지가, 실패하면 로그인 페이지에서 오류창을 띄워주게 된다.

 

<%

	String sessionId = null;
	
	HttpSession session = request.getSession(false);
	if(session != null){
		if(session.getAttribute("member")!=null){
			Member mb = (Member) session.getAttribute("member");
			sessionId = mb.getId();
		}
	}
    
%>

...(중략)

	<% if(sessionId == null) { %>
		<li class="nav-item"><a class="nav-link" href="member_login">로그인 </a></li>
		<li class="nav-item"><a class="nav-link" href="member_add">회원 가입</a></li>
	<% } else { %>
		<li style="padding-top: 7px;">[<%=sessionId%>님]</li>
		<li class="nav-item"><a class="nav-link" href="member_logout">로그아웃 </a></li>
		<li class="nav-item"><a class="nav-link" href="member_update">회원 수정</a></li>
	<% } %>

 

   이제 로그인을 한 후, 세션이 유지된 상태로 메인화면을 요청하면 '로그인/회원가입' 이 있던 탭에 현재 접속중인 계정의 아이디와 '로그아웃/회원수정' 이라는 항목이 나타나게 된다. 이런 식으로 request객체를 이용하여 받아온 변수나, 요청시 보내오는 파라미터의 여부로 같은 페이지라도 다른 내용을 보여줄 수 있는 것이다.

 

   이제 로그인을 하면 긴 시간 세션이 유지되는 상태이니, 게시판을 만들고 글을 작성할 수 있는 코드를 만들어보려한다.

   일단 로그인을 하지 않으면 게시판의 글쓰기 버튼에 접근할 수 없도록 유효성 검사와 sendRedirect를 통해 로그인 창으로 보내는 컨트롤러부터 제작해보았다.

 

@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException {
		System.out.println("BoardWrite 진입");
		
		HttpSession session = req.getSession(false);
		
		if(session == null) {
			System.out.println("세션이 존재하지 않습니다.");
			resp.sendRedirect("member_login");
		} else {
			RequestDispatcher rd = req.getRequestDispatcher("writeForm.jsp");
			rd.forward(req, resp);
		}
		
	}

 

   유효성 검사는 사실 Java에서 수행할 것이 아니라, HTML 자체에서 자바스크립트를 활용하여 하는 편이 좋다. Java가 할 수 없는 것은 아니나, 컨트롤러는 서버 내에서 이동하거나 혹은 나갔다가 들어오는 과정을 거치게 된다. 따라서 유효성 검사를 통해 서버 내를 활보하는 것은 그다지 효율적인 일이 아니며, 로그인의 여부나 form태그의 입력 여부 등은 서버로 전송되지 않고도 HTML 자체에서 처리할 수 있기 때문에 스크립트를 이용하는 것이 맞다.

 

   이제 게시판의 글쓰기에 접근하기 위해서는 세션을 가지고 있어야만 가능하다. 다음 시간에는 게시판 작성을 알아보자.

     데이터 베이스 작성     

create database bookmarketdb;
use bookmarketdb;

create table if not exists book(
	b_id varchar(10) not null primary key,
	b_name varchar(20),
	b_unitPrice integer,
	b_author varchar(20),
	b_description text,
	b_publisher varchar(20),
	b_category varchar(20),
	b_unitsInStock long,
	b_releaseDate varchar(20),
	b_condition varchar(20),
	b_filename varchar(20)
) default charset=utf8;

 

   BookMarket에 등록될 책들의 정보를 저장할 데이터 베이스를 만들고 book 테이블을 추가했다. 원래 데이터베이스와 DTO객체의 변수명은 통일시키는 것이 좋은데, 교재를 무심코 따라하다 보니 다른 변수명을 가지게 됐다. 나중에 연결할 때는 확인해서 설계단계를 꼼꼼히 거치도록 해야겠다.

 

     데이터베이스 연결하기     

private static Connection DBconn() {
		Connection conn = null;
		
		try {
			
			Class.forName("com.mysql.jdbc.Driver");
			
			String url = "jdbc:mysql://localhost:3306/bookmarketdb";
			String user = "root";
			String pw = "1234";
			
			conn = DriverManager.getConnection(url,user,pw);
			
			System.out.println("데이터 베이스 연결 성공");
		} catch(Exception e) {System.out.println("데이터 베이스 연결 실패");}
		
		return conn;
	}

 

   forName() 함수를 통해 드라이버를 로딩한 후, Connection 객체를 사용하여 데이터 베이스에 연결한다. CRUD를 돌리기 위해서 제일 먼저 되어야 하는 기능이므로 항상 코드를 작성하는 것 보다 함수로 만들어 필요할 때 호출하도록 만들었다.

 

     모델(BookRepository)     

	private BookRepository() {} //기본 생성자
	
	private static BookRepository repository = new BookRepository();
	//생성자 호출을 미리 해둠 (싱글톤)
	//private - 외부에서 접근을 막기 때문에, 함수도 같이 생성되나 접근할 수 없음
	
	public static BookRepository getRepository() {
		return repository;
		//생성된 객체를 반환
	}

 

    CRUD를 제대로 배우기 전, MVC 수업만 들을 때 까지만 해도 나는 데이터베이스는 모델이랑 같은 의미인 줄 알았다. 하지만 데이터베이스와 모델은 엄연히 다른 것으로, 데이터 베이스는 말 그대로 데이터를 보관하는 저장소지만 모델은 데이터 베이스로 가기 위한 임시 저장소에 가깝다고 보면 될 것 같다. 따라서 Java에서 보내줄 데이터를 임시로 저장하기 위한 공간을 매번 새로 생성할 이유는 없으니 싱글톤 패턴을 사용하여 모델을 하나로 유지하는 작업을 진행했다.

 

   기본 생성자 또한 다른 곳에서 생성될 이유가 없으므로 접근 제어자로 외부의 사용을 막고, 대신 모두가 접근 가능한 함수를 생성하여(public static) 고정된 저장소를 new 할 수 있도록 만들어준다.

 

     Read 컨트롤러     

	@WebServlet("/products")
	public class Read_Controller extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException {
		System.out.println("전체상품 컨트롤 페이지 (Read)");
		
		BookRepository dao = BookRepository.getRepository();
		ArrayList<Book> arr = dao.getAllBooks();
		
		req.setAttribute("list", arr);
		RequestDispatcher rd = req.getRequestDispatcher("books.jsp");
		rd.forward(req, resp);
	}

 

   이제 HTML에서 <a>태그나 <form>태그를 통해 요청이 발생하면 @webServlet() 으로 매핑된 컨트롤러 클래스에 도착하게 된다. 맨 처음 모든 상품을 보여주기 위한 컨트롤러를 위와 같이 작성했다. 먼저 전체상품을 사용자의 화면에 보여주려면 도서들의 정보가 필요하다. 이 정보는 모두 데이터베이스에 저장되어 있으므로 모델을 통해 데이터베이스에 접근하여 도서들의 객체 정보를 DTO로 담은 ArrayList 배열 형태로 받아온다. 이를 req(request)에 set() 함수를 통해 저장하고, forward함수로 해당 jsp 페이지로 이동하면 데이터베이스에서 그대로 받은 정보를 HTML에 전송할 수 있게 된다.

 

     Read :: getAllBooks     

	public ArrayList<Book> getAllBooks() {

		//해당 메서드는 도서 목록을 반환
		
		PreparedStatement ps = null;
		ResultSet rs = null;
		ArrayList<Book> list = new ArrayList<Book>();
		
		try {
			Connection conn = DBconn();
			
			String sql = "select * from book";
			ps = conn.prepareStatement(sql);
			rs = ps.executeQuery();	
			System.out.println("전체 도서 목록을 가져옵니다.");
			
			while(rs.next()) {
				Book book = new Book();
				
				book.setBookId(rs.getString("b_id"));
				book.setName(rs.getString("b_name"));
				book.setUnitPrice(rs.getInt("b_unitPrice"));
				book.setAuthor(rs.getString("b_author"));
				book.setDescription(rs.getString("b_description"));
				book.setPublisher(rs.getString("b_publisher"));
				book.setCategory(rs.getString("b_category"));
				book.setUnitsInStock(rs.getLong("b_unitsInStock"));
				book.setReleaseDate(rs.getString("b_releaseDate"));
				book.setCondition(rs.getString("b_condition"));
				book.setFilename(rs.getString("b_filename"));
				
				System.out.println("확인용 아이디"+rs.getString("b_id"));
				
				list.add(book);
			}
			
			rs.close();
			ps.close();
			conn.close();
			
		}
		catch(Exception e) { System.out.println("전체 도서 불러오기 실패"); }
		
		System.out.println("반환 완료");
		return list;
	}

 

   전체 도서 목록을 반환하는 함수이다. 모든 정보를 반환하기 때문에 따로 파라미터를 받을 건 없으며, 목록을 ArrayList로 반환해야 하기 때문에 반환타입이 존재한다. 이제 데이터베이스에 연결하는 함수는 이미 작성해두었으니 Connection 객체를 가져온 다음 연결되어 있는 데이터베이스에 SQL로 명령문을 내려야 한다.

 

   이때 명령문을 전달하기 위해서는 Statement객체를 사용해야 하는데, Statement객체보다는 PreparedStatement를 사용하는 것을 권장한다(객체생성의 문제가 있다고 한다). 전체 도서목록을 가져오기 위해서는 테이블에 저장되어 있는 모든 데이터를 가져와야하기 때문에 "SELECT * FROM (테이블 명)" 을 통해 모든 내용을 불러온 후, Connection 객체의 도움을 받아 Java에서 데이터베이스로 명령어를 전송해준다. 이 때 Statement객체와 PreparedsStatement객체의 사용법이 아주 살짝 차이가 있으니 알아두도록 하자. Statement객체는 명령어를 전송할 커넥션 객체를 생성한 후, execute문을 통해 명령어를 전송하지만 PreparedStatement는 커넥션 객체를 생성할 때 명령어를 전송하고 이후 execute문으로 실행을 한다.

 

   이렇게 객체를 통해 명령어가 전송되면 SQL에서 반환해주는 데이터를 ResultSet 객체를 통해 다시 받아온다. ResultSet객체의 내용은 데이터베이스에서 보여주는 엑셀 화면과 비슷하다 생각하면 쉽다. 그래서 모든 데이터를 한 줄씩(행) 읽어와야 하는데, 이 때 사용하는 함수가 next() 함수이다. 1행, 2행, 3행... 하나씩 넘기며 데이터가 있는지 확인하고 데이터가 존재하면 TRUE, null데이터를 만나면 FALSE를 반환하여 while문을 빠져나오게 된다.

   ResultSet에서 size()나 length() 함수를 사용하지 못하는 이유는 크기를 반환해주지 않기 때문이다. 따라서 데이터가 나오지 않을 때까지 다음줄로 넘기는 next()함수를 사용하는 것이다.

 

   이렇게 반복문을 통해 정보를 받아올 때마다 컨트롤러에 반환해야하는 ArrayList객체에 add()함수를 사용하여 불러온 객체를 저장한다. 이 때, 반복문은 다시 처음부터 실행하기 때문에 DTO객체는 계속해서 새로운 주소를 생성하여 데이터를 저장하기 위해서 반복문 안에서 객체 생성을 했고, ArrayList는 이 내용물이 그대로 유지되어야 하기 때문에 반복문 밖에서 생성해야 한다.

 

     Read JSP     

<% ArrayList<Book> listOfBooks = (ArrayList<Book>)request.getAttribute("list"); %>

<div class="row align-items-md-stretch text-center">
	<%
		for(int i=0; i<listOfBooks.size(); i++){
			Book book = listOfBooks.get(i);
	%>
			
	<div class="col-md-4">
		<div class="h-100 p-2">
			<img src="resources/img/<%= book.getFilename() %>">
			<h5><b><%= book.getName() %></b></h5>
			<p><%= book.getAuthor() %></p>
			<p><%= book.getPublisher() %> | <%= book.getReleaseDate() %></p>
			<p><%= book.getDescription() %>...</p>
			<p><%= book.getUnitPrice() %>원</p>
			<p>
				<% if(edit.equals("update")) { %>
                <a href="./update?id=<%=book.getBookId()%>"> 수정하기 &raquo;
				<% } else if(edit.equals("delete")) { %>
                <a href="#" onclick="deleteConfirm('<%=book.getBookId()%>')">도서삭제 &raquo;
				<% } else { %>
                <a href="./book?id=<%=book.getBookId()%>"> 상세 정보 &raquo; <% } %>		
			</a></p>
		</div>
	</div>
	<%
		}
	%>
</div>

<script type="text/javascript">
	function deleteConfirm(id) {
		if(confirm("!!해당 도서를 삭제합니다!!")==true){
			location.href = "deleteBook?id=" + id;
		} else { return; }
	}
</script>

 

   이제 받아온 정보를 HTML로 바꾸어 클라이언트 화면에 내보내는 작업을 진행했다. 컨트롤러에서 받아온 리스트를 다시 get()함수를 통해 받아주고(Object로 저장됐기 때문에 형변환 필요), ArrayList는 크기를 알 수 있으므로 for문 사용으로 꺼내는 것이 가능하다. 필요한 정보를 양식에 맞게 HTML 형식으로 작성하면 전체 도서 목록을 반환하는 페이지가 완성된다.

 

   데이터베이스와 Java를 연결하는 법을 배웠으니 이제 CRUD의 기본을 이해해 볼 차례이다. CRUD란 데이터 베이스에 생성/읽기/수정/삭제의 작업을 나열한 것이다. MVC의 구조에서 클라이언트가 발생시킨 요청이 컨트롤러에 도착하고, 컨트롤러는 다시 모델로 들어가 모델에서는 데이터베이스에 연결되어 최종적으로 요청에 맞는 값을 들고 다시 컨트롤러로 돌아온 뒤 클라이언트에 뷰로 전송하는 과정을 거친다. 이 때, 모델과 데이터베이스 사이에서 일어나는 정보의 교환을 통틀어 CRUD라고 칭할 수 있겠다.

 

     데이터베이스 연결하기     

private Connection DBconn() throws Exception{
		//Step1 JDBC 드라이버 로딩
		//특정 클래스를 찾아주는 역할
		Class.forName("com.mysql.jdbc.Driver");
		
		//Step2 connection 객체 생성
		//주의1 !데이터베이스 생성여부!
		//주의2 !WEB-INF/lib 폴더에 jar파일 있는지 확인!
		Connection conn = null;
		
		String database = "jdbc:mysql://localhost:3306/login_crud";
		//데이터베이스의 주소와 테이블 명
        
		String id = "root";
		//데이터베이스의 아이디
        
		String password="1234";
		//데이터베이스의 비밀번호
        
		conn = DriverManager.getConnection(database,id,password);
		//Connection 객체에 주소-아이디-비밀번호의 값을 넣어 연결 완료

		return conn;
        
	}

    

   본격적으로 CRUD를 위한 함수 작성에 앞서, 해당 작업은 모두 데이터 베이스와 연결이 되어 있어야 하는 상태이므로 필수로 연결하는 과정이 필요하다. 제일 먼저 Java와 데이터 베이스(SQL) 사이에서 번역기의 역할을 해줄 JDBC 드라이버를 불러와야 한다. 그리고 이를 연결시켜주는 Connection 객체를 사용하여 해당 함수에 주소/아이디/비밀번호를 입력하면 연결은 끝난다. 이 때, Connection 객체는 모든 CRUD함수에서 사용되어야 하므로 리턴 타입은 Connection 객체로 해주어야 한다.

 

 

     CREATE (생성하기)     

   이제 연결준비는 끝났으니 HTML의 요청에 따라 생성하는 함수를 작성해보자. 단순하게 아이디 / 비밀번호 / 나이를 받아오는 회원가입 폼을 만들고, 전송한 뒤 값을 컨트롤러로 넘어온 상태 라고 가정하겠다.

		req.setCharacterEncoding("utf-8");
		
		//Step01 전처리
		String id = req.getParameter("id");
		String pw = req.getParameter("pw");
		int age = Integer.parseInt(req.getParameter("age"));
		
		//데이터 묶음 처리
		member_dto dto = new member_dto();
		dto.setId(id);
		dto.setPw(pw);
		dto.setAge(age);

		//Step02 모델이동
		member_repository mr = member_repository.getInstance();
		mr.member_create(dto);
		
		//Step03 뷰이동
		resp.sendRedirect("readAll");

   

   먼저 <form> 태그를 통해 넘어온 값을 컨트롤러에서 Java가 사용할 수 있도록 저장하는 작업이 필요하다. 아이디와 비밀번호는 문자열로 받고, 나이는 숫자로 받아야 하지만 request에 저장되는 객체는 모두 String타입이기 때문에 parseInt를 통하여 형변환을 해주어야 한다. 이렇게 받아온 데이터는 다시 모델로 보내져 데이터베이스에 등록하는 과정을 거치게 되는데, 이 과정에서는 요청 발생 후 돌아갈 View가 없기 때문에(return이 필요하지 않음) 모든 데이터 베이스를 들고와 보여주는 Read 작업을 실행하게 된다.

 

public void member_create(member_dto dto) {
		
	try {
		Connection conn = DBconn();
		//Connection타입으로 받아와서 사용함

		Statement stmt = conn.createStatement();
		//java.sql.Statement import할 것
		//SQL문을 작성할 수 있도록 도와주는 개체
			
		String user_id = dto.getId();
		String pw = dto.getPw();
		int age = dto.getAge();
			
		String sql = "insert into member values('" + user_id + "','"+ pw +"'," + age + ")";
		stmt.executeUpdate(sql);
		//업데이트를 진행
			
	} catch (Exception e) { System.out.println("데이터 베이스 연결 실패(오류)"); }
	
}

 

   함수를 통해 dto 객체를 모델로 전송했다. 가장 먼저 데이터 베이스에 연결하기 위해 앞서 생성해두었던 함수를 호출하여 Connection객체를 생성하고, 이 커넥션을 통하여 SQL문을 작성할 수 있도록 도와주는 Statement 객체를 생성한다. 이제 컨트롤러에서 가져온 dto 객체에서 필요한 값들을 꺼내어 SQL에 작성할 명령어를 String 객체에 저장해준다. <form>태그로 받아온 회원정보를 데이터베이스에 처음 등록하는 과정이니까 insert를 사용하고, member 테이블에 값들( 아이디, 비밀번호, 나이 ) 을 담아준다.

 

   여기서 유의해야 할 점은 작은 따옴표(' ')의 사용인데, SQL에서는 문자나 문자열은 모두 작은 따옴표 내에 작성하게 되어있다. 그것이 규칙이므로(큰 따옴표를 사용해도 되지만, Java에서 큰 따옴표는 문자열로 해석하기 때문에 Java → SQL로 넘어가기 위해서는 작은 따옴표로 고정하여 사용한다) Java에서 작성할 때 따옴표 사용에 주의해서 주소를 적어주어야 한다. 어떻게 생성이 되었는지 모르겠다면 콘솔창에 해당 변수를 띄워보는 것도 방법이다.

 

   이렇게 SQL에 내려질 명령어를 String에 담았다면 executeUpdate() 함수를 통해 데이터베이스에 명령을 내린다. 이것이 CREATE(생성)의 흐름이다.

 

 

     Read (읽어오기)     

   CREATE(생성)이 완료되었다면 받을 return값은 없다(라기보단 필요가 없다, 몇 줄이 삽입되었는지에 관한 갯수를 반환하기 때문이다). 따라서 사용자의 요청에 따라 삽입된 것을 바로 확인할 수 있도록 전체회원을 조회해주는 HTML을 작성하여 response를 통해 다른 페이지로 보내줘야 한다. 이제 이 페이지에서는 데이터 베이스를 불러와 내용을 가져오는 작업을 해보자.

public ArrayList<member_dto> getAllmember(){
		
		ArrayList<member_dto> arr = new ArrayList<member_dto>();
		
		ResultSet rs = null;
		//데이터베이스가 보내주는 데이터를 받을 장소
		
		try {
			Connection conn = DBconn();
			Statement stmt = conn.createStatement();
			String sql = "select * from member";
			
			rs = stmt.executeQuery(sql);
			//데이터베이스에서 ResultSet이라는 객체에 담아 Java로 전송해준다
		
		
			//다음행이 있으면 True
			while(rs.next()) {
				
				String id = rs.getString("id"); //컬럼명
				String pw = rs.getString("pw");
				int age = rs.getInt("age");
				
				member_dto dto = new member_dto();
				dto.setAge(age);
				dto.setId(id);
				dto.setPw(pw);
				//dto.setAge(rs.getString("id")); 바로 담아도 상관 없음
                
				arr.add(dto);
				
			}
		} catch(Exception e) { System.out.println("불러오기 실패"); }
		
		return arr;
	}

 

   <form> 태그로 전송되어 저장된 멤버의 단위는 dto라는 객체 하나에 담겨있다. 전체 회원 조회를 위해서는 이 객체를 여러개 받아와야 하는데, 이를 한 곳에 묶어 컨트롤러로 내보내기 위해서는 객체를 담을 수 있는 컬렉션 프레임워크(여기서는 ArrayList)를 사용해야 한다. 따라서 dto만 담길 ArrayList를 생성한 뒤, 방금과 같이 데이터 베이스에 접근하기 위한 함수를 생성한다.

 

   다만 여기서는 추가만 했던 방금과는 다르게 데이터 베이스가 보내주는 데이터를 받아와서 저장해야 한다. 이 때 SQL에서 보내오는 데이터는 모두 ResultSet 이라는 객체에 담아오게 되는데, 해당 객체의 형태는 엑셀문서처럼 행 단위로 이루어진 텍스트 파일을 보낸다고 생각하면 편하다. 따라서 ResultSet이라는 객체에서 내용물을 하나씩 불러오기 위해서는 while문을 통해 한 줄씩 꺼내어 dto객체에 담고, 이를 다시 ArrayList에 추가하여 최종적으로 컨트롤러에 모든 정보를 전송할 수 있게 된다. 저장할 때는 executeUpdate() 함수를 사용했지만, 반대로 받아오기 위해서는 executeQuery() 함수를 사용한다.

 

   while문은 조건식이 true면 동작하는 구조를 가지고 있다. next() 함수를 통해 다음 행(데이터)이 존재 하는지에 대한 여부를 true/false로 반환하게 되는데, 마지막 데이터를 지나 eof(문서의 끝)를 만나게 되면 -1(false)를 반환하고 반복은 종료되게 된다. true가 실행되면 ResultSet이 가지고 있는 행 한 줄에서 get함수를 통해 데이터를 가져올 수 있다. 이 때 데이터 타입은 SQL에서 컬럼을 지정할 때 사용한 데이터 타입으로 받아와야 하며, 함수의 내용에는 문자열로 컬럼명을 입력하면 된다.

 

   이렇게 받아온 데이터는 다시 dto 객체에 담기 위해 반복 때마다 new로 생성하고 그 안에 데이터를 set한 뒤, ArrayList에 추가해주면 전체 회원을 모두 담는데 성공할 수 있다. 이렇게 가져온 데이터는 컨트롤러로 전송되고, 이는 다시 HTML(JSP)로 전송되며 최종적으로 아래의 코드를 실행해주면 데이터 베이스에 등록된 모든 회원들을 가져올 수 있다.

	<%
		ArrayList<member_dto> arr = (ArrayList<member_dto>)request.getAttribute("list");
		for(int i=0; i<arr.size(); i++){
					
			member_dto mb = arr.get(i);
	%>
			<tr>
				<td><%= mb.getId() %></td>
				<td><%= mb.getPw() %></td>
				<td><%= mb.getAge() %></td>
				<td> <a href="update?id=<%= mb.getId()%>">수정</a> </td>
				<td> <a href="delete?id=<%= mb.getId()%>">삭제</a> </td>
			</tr>	
					
	<%	
		}
	%>

 

   request객체에 setAttribute로 저장하는 것은 모든 타입을 무시하고 조상인 Object형식으로 저장되기 때문에 getAttribute로 가져올 때는 반드시 형변환 작업을 거쳐야 한다. 이렇게 받아온 List를 for문을 통해 객체 하나를 꺼내어 View 형식으로 작성하면 끝이다.

 

   데이터 베이스에 데이터를 추가하고 불러오기까지 했으니 이제 이 데이터를 수정하는 방법을 알아보자. 사용자가 수정하기 위해서는 기존 값을 들고와야 할 필요가 있다. 여기서는 MySQL에서 테이블을 만들 때, ID컬럼에 primary key 명령어를 추가하여 유일한 값이 되도록 설정해두었다. 따라서 필요에 의해 데이터를 조회할 때, 아이디를 사용하게 되면 사용자가 원하는 한 줄만 출력되게 지정할 수 있는 상태다. 하지만 중요한 점은 지금 필요한 행위가 '한 줄만 출력' 되게 한다는 점이다. 이 부분 역시 Read(읽어오기)에 관한 작업이기 때문에 다시 데이터를 불러오는 작업을 한번 더 복습해보자.

 

public member_dto getOnemember(String id) {
		
		ResultSet rs = null;
		member_dto dto = new member_dto();
		
		try {
			Connection conn = DBconn();
			Statement stmt = conn.createStatement();
			String sql = "select * from member where id='"+id+"'";
			rs = stmt.executeQuery(sql);
			
			if(rs.next()) {
				String userid = rs.getString("id");
				String pw = rs.getString("pw");
				int age = rs.getInt("age");
				
				dto.setAge(age);
				dto.setId(userid);
				dto.setPw(pw);
			}
			
		}
		catch(Exception e) { System.out.println("수정실패"); }
		
		return dto;
	}

 

   전체 조회를 할 때는 모든 정보를 얼만큼 가져올 지 모르기 때문에 while문을 통해 문서의 끝을 만날 때까지 반복하는 작업을 거쳤다. 하지만 이번에는 사용자가 수정을 위한 요청에 아이디를 담아 전송했기 때문에 데이터 베이스에서 하나의 값만 선택해서 가져올 수 있다. SQL문을 작성하기 위해 select * from member로 필요한 테이블을 선택한 후, where로 어느 행을 지정할 것인지 작성해주는데 이 때 유일한 값인 아이디가 들어가게 된다.

 

   따라서 이번에는 하나의 행만 가져올 수 있으니 while문으로 반복을 돌릴 필요가 없으며, next() 함수를 활용하여 행에 데이터를 가지고 있는지 조회한 후 가져온 데이터를 담아 dto 객체에 담아 return한다. 전체 조회는 여러개의 객체를 반환해야 하기 때문에 ArrayList에 담았고, 지금은 하나의 객체(행, 데이터)만 필요하기 때문에 dto에 담는 것이다.

 

	<%
		member_dto dto = (member_dto)request.getAttribute("dto");
	%>
	
	<form action="update" method="post">
		<p>ID : <input readonly type="text" name="id" value="<%= dto.getId() %>"> </p>
		<p>PW : <input type="text" name="pw" value="<%= dto.getPw() %>"> </p>
		<p>age : <input type="text" name="age" value="<%= dto.getAge() %>"> </p>
		<input type="submit" value="수정하기">
	</form>

 

   다시 컨트롤러에서 View로 전송되며, 사용자에게 수정 전의 데이터를 보여주기 위해 value로 미리 값을 보여준다. 여기서 ID는 유일한 값이며 변치 말아야 하니 readonly로 수정하지 못하게 막았고, 비밀번호와 나이를 수정할 수 있도록 해두었다.

 

 

     UPDATE(수정하기)     

   이제 사용자가 <form>태그를 통해 수정된 값을 다시 전송해 올 것이다. 그렇다면 이 데이터를 받아서 다시 SQL에 수정하라는 명령어를 전송하면 되는데, 이 과정은 CREATE와 유사한 방법이 되겠다. 따라서 처리하는 컨트롤러 또한 CREATE와 바뀐 점이 없다. 수정 요청을 처리한 후, 다시 내보낼 return값은 없기 때문에 마찬가지로 전체회원 목록을 보여주면 끝이니 sendRedirect를 통하여 기존에 만들어 둔 전체 회원 조회(Read)파트로 넘어가면 된다.

public void update_member(member_dto dto) {
		
		try {
			
			Connection conn = DBconn();
			Statement stmt = conn.createStatement();
			
			String sql = "update member set pw='"+ dto.getPw() 
			+ "',age="+ dto.getAge() 
			+ " where id='"+ dto.getId() + "'";
			
			stmt.executeUpdate(sql);			
			
		} catch(Exception e) { System.out.println("수정실패"); }
		
	}

 

   처음 생성하기를 진행했을 때 변수에 담아 명령어를 작성하였지만, 담지 않고 바로 함수로 호출하여 사용해도 문제가 없다. 따로 변수처리가 되지 않아 좀 더 간결해진 코드를 확인할 수 있을 것이다. 이번에는 insert가 아니라 기존에 있는 행의 데이터를 수정하기 위함이니 update member set으로 'member 테이블에서 바꿔' 달라는 명령어를 붙인 뒤, 아이디를 제외한 값들을 나열해준다. 그리고 where 명령어로 어느 행인지 아이디로 지정해주면 선택한 줄만 내용이 바뀌는 코드를 완성할 수 있다.

 

 

     DELETE(삭제하기)     

   이제 마지막으로 삭제하는 작업을 진행해보자. 방금과 같은 작업이 복잡하게 느껴질 순 있으나 기본적인 작동원리는 모두 비슷하다. 따라서 삭제 또한 하나의 행만 삭제하기 때문에 UPDATE와 같이 파라미터로 고유한 값인 ID를 가져와야 한다. 수정과 삭제 모두 <a>태그로 넘어올 때 ID 값을 가져왔으니 이를 컨트롤러에서 받아 모델로 넘어오기만 하면 뒤는 간단하다.

 	public void deleteUser(String id) {
		
		try {
			Connection conn = DBconn();

			Statement stmt = conn.createStatement();
			String sql = "delete from member where id='"+id+"'";
			
			stmt.executeUpdate(sql);

		} catch (Exception e) { System.out.println("삭제실패"); }
	}

 

   삭제 또한 생성과 수정처럼 return할 값이 없다. 따라서 void로 작성하였고, 수정때와 큰 차이는 없으며 대신 SQL에 내려야 할 명령어만 다른 상태이다. delete form member로 'member테이블을 삭제' 라는 명령어와 함께 where로 아이디 값을 입력하면 해당하는 아이디 값을 가진 행이 삭제되고 sendRedirect를 통하여 회원 조회 창으로 돌아오면 삭제가 되어있는 것을 확인할 수 있다.

 

   여기서 생성/수정/삭제 파트는 모두 sendRedirect를 사용하여 페이지를 이동시켰는데, 이는 forward와 다르게 서버를 완전히 나갔다가 다시 찾아 들어오는 구조기 때문에 데이터베이스의 변경사항이 실시간으로 반영되는 것처럼 보여지기 위함이다.

   MVC를 배우면서 저장소 부분은 임의로 자바에 만들어 사용했지만 사실은 서버를 나가서 다른 모델에서 들고오는게 일방적이다. 그래서 오늘은 간단하게나마 이런 저장소를 다뤄보는 시간을 가져보기로 했다.

 

MySQL 다운로드

https://downloads.mysql.com/archives/installer/

 

MySQL :: Download MySQL Installer (Archived Versions)

Please note that these are old versions. New releases will have recent bug fixes and features! To download the latest release of MySQL Installer, please visit MySQL Downloads. MySQL open source software is provided under the GPL License.

downloads.mysql.com

 

   데이터베이스는 데이터 파일의 중복을 피하고 조직적으로 구조화시킨 데이터의 모음이라고 보면 된다. 이러한 데이터 베이스는 단순히 입/출력을 반복하기 때문에 저장소 정도로만 생각할 수 있지만 실제로는 회사의 중요한 자산 중 하나로써 기능한다. 그렇다면 이런 중요한 자료를 어떻게 관리할 수 있을까? 그건 마이크로 소프트의 윈도우와 같이 하나의 운영체제로 사용할 수 있는데, 사용자 인터페이스에 특화되어 있는 운영체제가 아니다보니 데이터 베이스를 관리하기 위해서는 프로그램과 함께 이를 위한 전문가가 필요하다는 것이 단점이 될 수 있겠다.

 

   그렇다면 이제 위 사이트에서 다운받은 MySQL을 사용하여 데이터 베이스의 기초지식을 알아보도록 하겠다.

   MySQL은 데이터베이스의 표준 언어인 SQL을 사용하며, 오픈소스로 널리 사용되고 있는 프로그램이다.

SQL 문장 명령어 설명
데이터 정의 언어 (DDL) CREATE, ALTER, DROP Object관련 생성/수정/삭제를 할 수 있음
데이터 조작 언어 (DML) SELECT, INSERT, UPDATE, DELETE 데이터 관련하여 삽입/수정/삭제를 할 수 있음
데이터 제어 언어(DCL) GRANT, REVOKE, COMMIT 권한에 관련된 설정을 할 수 있음

 

 

   create database test9;

   먼저 데이터 베이스를 생성한다.

 

   show databases;

   생성을 하게 되면 콘솔창에도 완료 메세지가 뜨지만 확인하고 싶다면 해당 명령어를 사용하여 모든 데이터 베이스를 확인한다. 그러면 아래에 현재 가지고 있는 데이터베이스 목록이 뜨게 된다.

 

 

   use test9;

   생성을 했어도 바로 사용할 수 있는 것은 아니다. 물론 사용할 수 있지만 그건 테이블을 만든 아래에서 설명하겠다. 여기서 use명령어는 java에서 import문과 유사한 성격을 가지고 있다.

 

   create table kwangsu( id varchar(10), pw varchar(10), age int );

   여기서 말하는 테이블은 스프레드시트의 시트와 유사하다. 테이블은 하나의 시트가 되며, 나는 여기서 kwangsu라는 이름의 시트를 하나 만든 셈이다. 단, 생성만 한다고 끝이 아니며 해당 시트에 들어갈 열의 속성을 지정해주어야 한다. 이를 여기서 컬럼이라 부르며, id / pw / age 를 받을 수 있는 컬럼 세개를 지정해주었다. varchar는 일반적으로 쓰인 char를 뜻하는데, 괄호에 들어간 숫자는 최대 글자 수를 얘기한다. java와는 반대로 데이터 타입이 데이터 이름 뒤에 붙는다.

   만약 여기서 use명령어를 사용하지 않고 진행하려면 만들 테이블명 앞에 test9를 붙여야 생성할 수 있다.

   (ex. test9.kwangsu)

 

   show tables;

   위에서 데이터베이스가 만들어졌는지 확인하기 위해 사용한 명령어와 똑같다. 여기서는 만들어진 테이블을 보여준다.

 

   desc kwangsu;

   해당 테이블이 가지고 있는 컬럼 목록을 보여준다.

 

   insert into kwangsu(id,pw,age) values("a", "1234", "9");

   만들어진 테이블에 값을 집어넣는 명령어다. insert into 다음에 테이블 이름이 오되, 담을 값이 열거된 형태와 다르지 않다면 컬럼 부분은 생략할 수 있다. 테이블을 생성한 방식도, 값을 넣는 방식도 어딘가 함수와 닮아 있어 이해하기 어렵지 않았다.

 

   select id,pw,age from kwangsu;

   데이터가 잘 담겼는지 확인하기 위해서 select 명령어를 사용할 수 있다. from뒤에 오는 테이블에서 보고자 하는 컬럼을 선택하면 해당 컬럼만 보여준다.

 

   select * from kwangsu where pw="1234";

   위와같이 선택할 때 *(any)를 쓰면 모두 선택이 된다. 이후  where 명령어 뒤로 구체적으로 선택하고자 하는 컬럼값을 넣어주면 해당하는 값을 가진 행만 보여주게 된다.

 

   update kwangsu set age=29 where id='a';

   기존 값을 바꾸고 싶을 때 사용하는 명령어로, 테이블명 뒤에 바꿀 컬럼값을 적어준다. 그리고 위와 같이 행을 선택하기 위해 where 명령어를 사용하는데, 여기서 주의해야 할 점은 다중선택으로 바꾸는게 아니라 단일로 바꾸고자 할 경우에는 꼭 유일한 값(ex. 여권번호, 주민등록번호 등...)을 가진 컬럼을 지정해주어야 한다. 데이터베이스의 기본적인 기능은 중복을 피하는 것인데, 이를 지정하기 위한 명령어는 뒤에서 설명하겠다.

 

   delete from kwangsu where id='c';

   데이터를 삭제하기 위해서 사용하는 명령어이다. 화면에 보이는 것 처럼 '행'으로만 삭제할 수 있으며, 이 또한 위와 같이 where로 지정된 값이 삭제된다.

 

 

   drop table book;

   해당 테이블을 삭제할 때 쓰는 명령어다. drop뒤에 데이터베이스를 하면 해당 데이터 베이스를 지울 수 있다.

 

   b_id varchar(10) not null primary key

   테이블에 컬럼을 만들 때, null값이 허용되지 않는 컬럼을 지정할 수 있는데 그 명령어가 not null이다. 또, 위에서 말한 것 처럼 id와 같이 중복이 되면 안되는 값들을 관리하기 위해 primary key를 지정하는데, 이 명령어가 지정되어 있는 컬럼에서는 중복된 값이 허용되지 않는다.

 

   이제 MySQL을 활용하여 데이터를 저장하고 관리하는 것에 대해 배웠으니 JDBC를 이용하여 Java와 연결하는 법에 대해서 알아보고자 한다. JDBC는 Java 혹은 JSP 프로그램 내에서 데이터베이스와 관련된 작업을 처리할 수 있도록 도와주는 번역기라고 보면 이해하기 쉽다.

 

   Class.forName("com.mysql.jdbc.Driver")

   우리는 어떤 제품을 처음 설치하거나 사용할 때 반드시 드라이버를 필수로 사용하게 된다. 이는 제품과 프로그램 사이에서 틀어질 수 있는 언어를 번역하여 충돌을 방지하기 위함으로, Java와 SQL 또한 서로 다른 언어를 사용하기 때문에 이들 사이에는 번역기가 필요하다. Class.forName( )을 통하여 메모리에 해당하는 드라이버를 로드하면 준비는 끝난다.

 

   Connection conn = DriverManager.getConnection( 주소, 아이디, 비밀번호 )

   드라이버가 로드 되었다면 본격적으로 Java에서 데이터베이스를 연결하기 위해 Connection이라는 새로운 객체를 사용한다. 주소는 주소는 데이터베이스의 경로를 나타내고 아이디와 비밀번호는 MySQL을 설치할 적에 만들어둔 인스턴스의 정보를 말한다.

 

   이렇게 초기 설정을 잡아두면 연결을 완료된다. 아무런 로직을 사용하지 않고, 단순 연결확인을 위해 콘솔에 기록을 남겨보면 정상적으로 연결되는 것을 확인할 수 있다.

+ Recent posts