[Servlet] 회원가입 만들기

2025. 3. 4. 18:47·Web

포인트 : tab 회원가입, hidden 태그, 아이디 중복 체크, , Ajax 맛보기, model, 암호화, DB연결, DB에 값 저장, DB값 가져오기

 

join.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>쇼핑몰 회원가입 페이지</title>
</head>
<body>

<form id="frm" method="post" action="./join_ok.do">

<!-- 중복체크를 사용하여 사용가능한 아이디 결과를 받았는지 체크하는 속성 -->
<input type="hidden" id="idck" value="">

<p>회원가입</p>
<div>
<input type="radio" name="spart" value="P" checked="checked" onclick="frm_view(this.value)">일반회원
<input type="radio" name="spart" value="C" onclick="frm_view(this.value)">사업자회원
<br>
회원아이디<br>
<input type="text" name="sid"> <input type="button" value="중복체크" onclick="idcheck()">
<br>
회원 패스워드<br>
<input type="password" name="spw"><br>
회원 이름 및 회사명<br>
<input type="text" name="snm"><br>
휴대폰번호 및 전화번호<br>
<input type="text" name="stel" maxlength="11"><br>
이메일 주소<br>
<input type="text" name="semail"><br>

<!-- span을 이용해 일반회원일때 안보이게 하기
	span은 name을 사용하지 않음!
	입력하는 값들만 name 사용 -->
<span id="corp" style="display:none;">
사업자 번호<br>
<input type="text" name="sno" maxlength="13"> * "-" 없이 입력<br>
</span>

<br><br>
<input type="button" value="회원가입"  onclick="memberok()">
</div>
</form>

</body>
<script src="./join.js?v=1"></script>
</html>
  • html
    • 일반회원, 사업자회원 라디오 버튼으로 선택
      • 일반회원 선택시 사업자번호 입력란이 보이지 않음
      • 사업자회원 선택시 사업자번호 입력란이 보이게 됨
      • => 사업자번호 input 태그를 span으로 감싸 display 속성을 이용 (none, block)
    • 중복확인 클릭시 Javascript(join.js파일)의 idcheck()
      • => Ajax를 이용하여  servlet(idcheck.java파일)에서 return 받은 값으로 중복 체크
      • 아이디 중복체크를 했는지 안했는지 hidden속성의 태그를 사용하여 핸들링
    • 회원가입 클릭시 join_ok.do로 이동

join.js

function memberok(){
	/* 추가해야할것 
	전화번호 자릿수 형태 체크 
	사업자번호 자릿수 형태 체크 
	 */
	if(frm.sid.value == ""){
		alert("아이디를 입력 후 중복체크를 하세요!")
	}else if(frm.spw.value == ""){
		alert("패스워드를 입력하세요");
	}else if(frm.snm.value == ""){
		alert("이름 및 회사명을 입력하세요");
	}else if(frm.stel.value == ""){
		alert("휴대폰번호 및 연락처를 입력하세요");
	}else if(frm.semail.value == ""){
		alert("이메일을 입력하세요");
	}else{
		if(frm.spart[0].checked == true){	//일반회원일때 
			if(document.getElementById("idck").value == ""){
				alert("아이디 중복체크를 하셔야만 회원가입이 진행됩니다.");
			}else{
				frm.submit();
			}
		}else{	//사업자회원일때 
			if(frm.sno.value ==""){
				alert("사업자 번호를 입력하셔야 합니다.");
			}else if(frm.sno.value.length < 10){
				alert("올바른 사업자 번호를 입력하세요!");
			}else {
				frm.submit();
			}
		}
	}
}

//사업자등록번호 보이게 안보이게
function frm_view(part){
	var sp = document.getElementById("corp");
	//style.display : 해당 오브젝트를 웹에 출력 또는 미출력 
	if(part=="C"){
		sp.style.display = "block";
	}else{
		sp.style.display = "none";
	}
}

//아이디 중복체크 사항 
function idcheck(){
	if(frm.sid.value==""){
		alert("아이디를 입력해주세요.");
	}else{
		//Ajax 역할 (Back-end와 통신)
		ajaxpost(frm.sid.value);		
	}
}

//원래 안빼도 되는데 처음하니까 빼서 보여줌
//Ajax 함수를 이용하여 중복 체크 
var ok = "";	//Back에게 통신을 보내는 역할을 하는 변수 
function ajaxpost(data){
	//console.log(data);
	function wck(){
		//XMLHttpRequest : 현재 웹페이지에서 XHR 통신을 사용함 
		//실무에서 XHR 통신이라고 부름 
		//사수가 XHR 해봤냐? => Ajax해봤냐? 
		if(window.XMLHttpRequest){
			return new XMLHttpRequest();	//신규 XHR 통신 생성 
		}
	}
	
	function getdata(){
		//console.log(ok.readyState);	//1,2,3,4 출력됨 
		//XMLHttpRequest.DONE == 4
		if(ok.readyState==XMLHttpRequest.DONE && ok.status==200){
			console.log(this.response);	//Back-end 결과값을 받음 	
			
			if (this.response == "ok") {
				alert("사용가능한 아이디 입니다.");
				frm.sid.readOnly = true; // 아이디를 더 이상 수정하지 못하도록 읽기전용 변경
				document.getElementById("idck").value = "ok";
			} else if (this.response == "no") {
				alert("해당 아이디는 이미 사용 중입니다.");
				frm.sid.value = "";
			}
		}
	}
	
	//순서에 맞게 통신을 실행하는 역할 
	ok = wck();	//신규 XHR 생성 
	ok.onreadystatechange = getdata;	//해당 함수 결과를 받는 설정
	ok.open("GET","./idcheck.do?sid="+data,true);	//Back-end로 값을 이관
	//POST로 보낼땐 이렇게 안보냄  
	ok.send();	//Ajax 통신 실행 
}
      • memberok() : 빈값 체크, 아이디 중복체크 했는지 확인, form 전송 => join_ok.do
      • frm_view(part) : 일반회원을 체크했는지 사업자회원을 체크했는지 확인하여 사업자등록번호 입력란을 보이게, 안보이게 함
      • idcheck() : 아이디 중복체크를 위해 id값을 파라미터에 담아 ajaxpost()함수로 전송
      • ajaxpost(data) : Ajax 함수를 이용하여 아이디 중복 체크 => servlet(idcheck.do)으로 전송해서 return 값을 받아옴
        • XMLHttpRequest : 현재 웹페이지에서 XHR 통신을 사용함 => XHR = Ajax
        • XMLHttpRequest.UNSENT => 0 : 객체 생성 (위의 new부분)
          XMLHttpRequest.OPEN => 1 : XHR 통신 연결 (아래 ok.open()메소드)
          XMLHttpRequest.HEADERS_RECEIVED => 2 : Back-end URL 및 통신규격(get, post)
          XMLHttpRequest.LOADING => 3 : Back-end 경로 응답 대기 시간 
          XMLHttpRequest.DONE => 4 : Back-end가 요청된 데이터 처리 완료 결과값을 보냄
        • status : 통신에 대한 결과 코드 번호 (200:성공, 405:Back-end쪽 문제, 404:URL 경로 오류)
        • Ajax 통신규약 : POST, GET, PUT, DELETE ...
        • ok.open("통신규약", "Back-end 경로", true나 false) 기본이 true여서 true는 안써도됨 
          • true : 비동기통신 async
          • false : 동기통신 sync
          • 비동기통신 : 여러 데이터를 지속적으로 전송할 수 있으며 결과값을 따로 받을 수 있음
            •  더 빠르지만 에러가 발생할 경우 어디에서 발생한지 찾기가 힘듦
          • 동기통신 : 순차적으로 처리하는 방식 1:1
            • 순차적으로 처리하기 때문에 느리지만 에러를 찾을 수 있음 단,여러개의 값 처리시 어려움(?) => FORM 통신  

m_dbinfo.java(class)

데이터베이스 환경설정 Model

package shop;

import java.sql.Connection;
import java.sql.DriverManager;

//오라클이면 com.뒤에 오라클쓰면됨 
import com.mysql.cj.jdbc.Driver;
//임포트 해서 조금씩 치다보면 자동완성에 다 뜨니 외우지 않아도됨 

//Database, 사용자 아이디, 사용자 패스워드
//db이용시 주의사항 : static으로 메모리에 올려야함!!
public class m_dbinfo {
	
	//Database의 기본 메소드명 : getConnection
	//메소드 이름 바꿔도됨 
	public static Connection getConnection() throws Exception{
		//해당 라이브러리 복붙
		//해당 라이브러리를 가져오는 역할 변수 
		String db_driver = "com.mysql.cj.jdbc.Driver";
		
		//데이터베이스 프로퍼티스 가서 복붙해오기
		/*데이터베이스 경로 연결 
		jdbc:mysql:// => ip또는 도메인 
		: 포트번호 (현재 3306) //회사에선 보안때문에 도메인 바꿔서 씀 실무에서 여기 번호 따라서 바꾸기
		 */
		String db_url = "jdbc:mysql://localhost:3306/mrp";
		
		//mysql에 접속하는 사용자 
		String db_user = "project";
		
		//mysql에 접속하는 사용자의 패스워드 
		String db_passwd = "a123456";
		
		//실제 Database에 연결을 하는 명령어 
		Class.forName(db_driver);
		Connection con = DriverManager.getConnection(db_url,db_user,db_passwd);
		
		//안써도 되는데 한번 확인해보기 
		//데이터베이스 잘 연결됐는지 확인하기!
		//System.out.println(con);
		
		return con;	//데이터베이스가 정상적으로 연결의 되었는지 확인한 결과를 return
	}
}

 

  • 라이브러리 가져오는 변수 실무에서 사용하는 두가지 방법
    • 1. StringBuilder 사용
      • StringBuilder sb;
        sd.append();
    • 2. Map 사용 -> 보안강화 
      • Map<k, v></k, v>
    • 책에선 String을 사용하는데 보안쓰레기에 실무에서 쓰지도않음
      • 해당 파일에서는 String을 이용하여 db_driver변수로 사용함 
  • db driver는 import com.mysql.cj.jdbc.Driver; 임포트 자동완성 슬쩍슬쩍 쓰면됨
    • 여기 임포트된 부분 그대로 db_driver 변수에 싹 복붙하기
  • db 경로는 연결된 데이터베이스 우클릭 properties 해서 복붙하기

idcheck.java (servlet)

Ajax통신을 받는 메소드 + DB연결 모델 사용

package shop;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class idcheck extends HttpServlet {
	private static final long serialVersionUID = 1L;
	Connection con = null;
       
	//ajax 통신을 받는 메소드 (아이디 중복체크) 
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String msg = "";	//Front-end에게 결과값을 보내는 변수 
		//Back-end가 Front-end에게 결과값을 통보하는 역할 
		PrintWriter pw = response.getWriter();
		
		try{
			//Ajax로 Front-end가 보낸 데이터를 받는 역할 
			String id = request.getParameter("sid");
			
			//여기에 null일 경우를 핸들링하지않고 예외처리로 빼줘도 됨! 
			//if(id.equals("") || id.equals(null))
			if(id.equals("")) {	//여기 null만쓰면 공백 입력시 해당하지 않음!!
				msg = "error";
			}else {
				m_dbinfo db = new m_dbinfo();	//모델 로드
				//db사용시 try catch 필요 //싫으면 throws에 D어쩌고 추가 
				this.con = db.getConnection();	//Database 연결 시작
				
				/*
				sql query문 작성 방법 ⭐️ 외우기 
				1. select => ResultSet, executeQuery
				2. insert, update, delete => int, executeUpdate 
				*/
				//select sid from shop_member where sid='변수명';
				String sql = "select count(*) as ctn from shop_member where sid='"+id+"';";
				
				//Statement : Database에 쿼리문을 작성할 수 있도록 사용하는 메소드 
				//createStatement() : create, alter, drop //이거 보안 쓰레기라 사실 요즘 안씀  
				Statement st = this.con.createStatement();
				
				//ResultSet : 결과값을 받는 역할 select만 사용 
				//executeQuery : ResultSet와 세트 
				ResultSet rs = st.executeQuery(sql);	//select에 사용하는 명령어 
				
				//쿼리문 잘쓴건가 싶을때 그냥 찍어보기
				//System.out.println(sql);
				
				//조건문을 사용한 경우	(한번만 쓸 때 사용 => count나 sum (값이 하나만 나올때)사용시에만 사용) 
				if(rs.next()==true) {	//정상적으로 query문이 작동했을 경우 
					if(rs.getString("ctn").equals("0")) {	//해당 데이터가 있을 경우 //⭐연산기호 사용 불가능@! 꼭쓰고싶다면 .intern()쓰기 
						msg = "ok";
					}else {	//검색한 데이터가 있을 경우 
						msg = "no";
					}
					System.out.println(rs.getString("ctn"));
				}
				//else의 경우 (필요없음 오류발생시 exception으로 빠짐) 
                
				rs.close();
				st.close();
				/*
				//반복문을 사용한 경우 (모든 경우 사용 가능)
				while (rs.next()) {	//rs.next() : 결과값에 대해서 반복문이 실행 (true, false) 
					System.out.println(rs.getString("ctn"));
				}
				*/
			}
			pw.write(msg);
	
		}catch (NullPointerException ne) {	//Front-end가 올바른 값을 전달하지 않을 경우 
			msg = "error";
			pw.write(msg);	//ok : 사용가능 아이디, no : 사용불능 아이디, error : 데이터 오류
		}catch (Exception e) {
			//프론트가 무시할까봐 혼자 몰래 고치려고 ㅜㅜ 
			msg = "DBS error";	//실무에서 백엔드 잘못시 백엔드만 아는 비밀 메세지 ㅎ  "Mysql-CODE 844" : 선생님만의 sql쿼리 오류 비밀코드 
			System.out.println(e);
			pw.write(msg);
		}finally {
			pw.close();
		}		
	}
}
      • msg 변수에 여러 상황의 값을 담아 Ajax로 return해줌 => Front에게 값들의 의미를 알려줘야함 그래야 코드 짬 
        • id가 공백일때
          • error
        • id가 공백이 아닐때
          • Database를 연결해 SQL Query문을 이용하여 해당 id가 있는지 개수를 select로 검색 
          • SQL Query문이 정상작동했을때
            • DB에 해당 아이디가 있을 경우
              • ok
            • DB에 해당 아이디가 없을 경우
              • no
          • SQL Query문이 정상작동하지 않았을때
            • Exception으로 빠짐 
      • ⭐️SQL Query문 작성 방법
        • 1. select => ResultSet, executeQuery
        • 2. insert, update, delete => int, executeUpdate
      • SQL 쿼리문 적용방법
        • Statement : Database에 쿼리문을 작성할 수 있도록 사용하는 메소드
        • createStatement() : create, alter, drop //이거 보안 쓰레기라 사실 요즘 안씀  
        • ResultSet : 결과값을 받는 역할 select만 사용 
        • executeQuery : ResultSet와 세트
    •  
  •  

m_md5.java(class)

md5를 이용해 비밀번호를 암호화하는 Model

package shop;

import java.security.MessageDigest;

//패스워드 md5 암호화 생성 
public class m_md5 {
	public String md5_code(String pw) throws Exception{
		StringBuilder sb = new StringBuilder();
		String md5_data = "";	//사용자가 입력한 값을 md5로 암호화하는 변수
		
		MessageDigest md = MessageDigest.getInstance("md5");
		md.update(pw.getBytes());
		byte md5byte[] = md.digest();
		
		//암호화 알고리즘 
		for(byte alg : md5byte) {
			sb.append(String.format("%01x", alg));
		}
		md5_data = String.valueOf(sb);

		return md5_data;
	}
}

 

join_ok.java (servlet)

html값들을 받아 DB에 저장하는 메소드 + 비밀번호 암호화 모델 사용 

package shop;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//get + post 함께 있으면 servlet 단독 실행 가능 
public class join_ok extends HttpServlet {
	private static final long serialVersionUID = 1L;
	Connection con = null;	//Database 연결시 필수 (SQL Query 실행시 필요)
	
	/*
	//service : doGet, doPost 둘 다 받음 모두 사용 가능  
	//서블릿 파일 만들 때 넥스트넥스트하면 서비스있음
	//대신 service는 보안이 제일 약함 !! => 테스트할때 테스트용으로만 사용  
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("post, get 모두 받음");
	}
	*/
	
	/*
	 //DB 기본 문법 (회원가입하는데 doGet 쓰면 작살난다)
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		m_dbinfo db = new m_dbinfo();	//실행 명령어
		try {
			this.con = db.getConnection();
		}catch (Exception e) {
			System.out.println("DB 연결 실패");
		}finally {
			try {
				this.con.close();				
			}catch (Exception e) {
				System.out.println("DB가 올바르게 해제되지 않았습니다.");
			}
		}
	}
	*/
	
	PrintWriter pw = null;
	PreparedStatement ps = null;
	//회원가입 진행 
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html;charset=utf-8");	//여기 오타나면 다따운받아서 코드 유출됨 주의
		this.pw = response.getWriter();
		m_dbinfo db = new m_dbinfo();
		
		try {
			this.con = db.getConnection();
			String spart = request.getParameter("spart");
			String sid = request.getParameter("sid");
			String spw = request.getParameter("spw");
			String snm = request.getParameter("snm");
			String stel = request.getParameter("stel");
			String semail = request.getParameter("semail");
						
			spw = new m_md5().md5_code(spw);	//문자 패스워드를 MD5로 암호화 
			String sql = "";	//Database Query문  

			if(spart.equals("P")) {	//개인회원
				sql = "insert into shop_member values('0',?,?,?,?,?,?,null,now())";
				this.ps = this.con.prepareStatement(sql);
				this.ps.setString(1, spart);
				this.ps.setString(2, sid);
				this.ps.setString(3, spw);
				this.ps.setString(4, snm);
				this.ps.setString(5, stel);
				this.ps.setString(6, semail);
				int result = this.ps.executeUpdate();
				if(result > 0) {	//정상적으로 DB에 insert되었을 경우 
					this.pw.write("<script>"
							+ "alert('회원가입이 정상적으로 완료되었습니다.');"
							+ "location.href='./login.jsp'"
							+ "</script>");
				}
			}else {   //사업자회원 
	            String sno = request.getParameter("sno");
	            sql = "insert into shop_member values('0',?,?,?,?,?,?,?,now())";
	            this.ps = this.con.prepareStatement(sql);
	            this.ps.setString(1, spart);
	            this.ps.setString(2, sid);
	            this.ps.setString(3, spw);
	            this.ps.setString(4, snm);
	            this.ps.setString(5, stel);
	            this.ps.setString(6, semail);
	            this.ps.setString(7, sno);
	            int result = this.ps.executeUpdate();
	            if(result > 0) {   //정상적으로 DB에 insert되었을 경우 
	               this.pw.write("<script>"
	                     + "alert('사업자 회원가입이 정상적으로 완료되었습니다.');"
	                     + "location.href='./login.jsp'"
	                     + "</script>");
	            }
	         }
		}catch (java.sql.SQLIntegrityConstraintViolationException se) {
			this.pw.write("<script>"
					+ "alert('연락처 및 이메일이 중복되어서 가입이 취소되었습니다..');"
					+ "history.go(-1);"
					+ "</script>");
		}catch (Exception e) {
			System.out.println(e);
		}finally {
			try {
				this.ps.close();
				this.con.close();
				this.pw.close();				
			}catch (Exception e) {
				System.out.println(e);
			}
		}
	}
}
      • DB에 값 저장하기
        • Connection : Database 연결시 필수 
        • PreparedStatement : sql query문에 순차적으로 값을 삽입 
        • executeUpdate() : db에 저장
      • 쿼리문 작성 두가지 방법
        • 1. createstatement 방식 => SQL인젝션공격에 취약함 
          String sql = "insert into 테이블명 values('"+id+"','"+pw+"')";
        • 2. preparedstatement 방식 => SQL인젝션공격을 방지함 보안굿 / 실무에서 사용 / 무조건 이거쓰기
          String sql = "insert into 테이블명 values(?,?)";

 


[Servlet - Database]
1.Model - Database 연결, DTO(getter, setter), security(md5,sha)
2. Controller - Web(가상) - Backend (Post, Get)
3. View - JSP (출력)

회원가입 + 로그인
1. Database 연결 - 모든 프로젝트에 한개의 파일로 연결(Database 1개)
2. DTO - Table 별로 구성
3. security - 한 가지의 보안 설정 

IDE - setting
1. Database 라이브러리 셋팅 => /src/main/webapp/WEB-INF/lib 안에 
mysql-connector-java-8.0.30 => mysql.8.x
mysql-connector-java-5.1.x => mysql.5.1
mysql-connector-java-6.x. => mysql.5.7

[Model] - 직접 실행이 안됨 (Controller에서 실행시켜야 함)

=====

[회원가입 - Tab] - SPA (회원가입)
ex) cafe24
가입시 일반회원, 사업자회원을 나눠서 가입하는 방식을 
Front-end에서는 spa (싱글페이지계정관리설정) 이라고 부름

[Database Table]
create table shop_member(
sidx int not null auto_increment,
spart enum('P','C') not null,
sid char(50) not null,
spw varchar(25) not null,
snm char(40) not null,
stel char(11) not null,
semail varchar(50) not null,
sno char(13) null,
sdate timestamp not null default current_timestamp,
primary key(sidx),
unique (sid),
unique (stel),
unique (semail)
);

=> 주의사항 
md5 암호화 길이 40정도 사용 
이름은 회사이름을 고려하여 길게 잡기, 검색가능하게 char 사용 (40)
사업자번호 길이는 개인 10자리 법인 13자리! 
자릿수확인은 front가 아니라 back이 해야함 
일반회원을 고려하여 null을 사용!
unique를 걸면 안됨 일반사용자가 null로 겹침 

=> 이클립스에서 테이블 생성
Data Source Explorer > table에서 오른쪽 위 open scrapbook to edit SQL statements > 붙여넣기 > 우클릭 > Execute all > 적용 > refresh 후 확인 
확인 : 작성한 테이블 > colums 에서 보이는 것들을 front에게 알려주기 

=====

 

[form과 ajax차이]
form은 url이 바뀜
ajax는 url그대로 해서 back으로 쏴줌 (javascript에서만 사용 가능)

ajax 통신 (Javascript로 web통신을 하는 역할 - 비동기 통신)
Front-end : post? get? Back에게 물어봄
Back-end : Ajax 통신 (post,get) 선택
controller를 생성 - post,get
결과값을 Front-end에게 보내줌 (안보내주면 Front-end는 아무고토못함)

Form 통신 (HTML 기본)
Front-end -> post,get -> Back-end 전달

ajax 통신
Front-end -> post,get -> Back-end 전달 받은 후 처리 결과를 return -> Front-end가 결과값을 받음 

ajax 통신 확인하기
웹페이지 > 개발자도구 > Network > Fetch/XHR 
Status 200의 의미는 XHR로 값이 잘 전달됐다는 뜻
get,post등 값을 잘못 전달 => 405 
300,400 대 => 에러
name아래 하나 눌러서 Headers에는 정보가 담기고 Response에는 Back에서 던진 값 출력 

ajax통신 11가지 존재 다 알아야 프론트의 값을 핸들링 가능 

=====

회원가입 + 로그인 세트
게시판 글쓰기 수정 삭제 세트

[전체 Process]
1. 기획서 내용을 확인
2. Database Table 설계
3. 중복확인 부분 분류 => 무조건 Ajax 써야하기때문
4. HTML 제작 (name="컬럼명")
5. Controller 제작
6. Database Connection (Model) 제작 
7. 암호화 모듈을 사용할 경우 (Model)
8. Ajax를 활용하는 interface일 경우 => Controller를 제작 
9. Javascript로 코드를 작성
10. Form으로 데이터 전송 => Controller
11. Controller => Database에 저장시킴

















 

 

 

 

 

 

 

 

 

 

 

 

 

저작자표시 비영리 변경금지 (새창열림)
'Web' 카테고리의 다른 글
  • [Javascript] 정규식 코드
  • [Servlet] 로그인, 로그아웃 만들기
  • HTML + js + servlet + java + MySQL
  • Servlet - 응용문제 [쿠폰 등록 시스템]
9na0
9na0
응애
  • 9na0
    구나딩
    9na0
  • 전체
    오늘
    어제
    • 분류 전체보기 (211)
      • Web (118)
      • Java (28)
      • 데이터베이스 (14)
      • 세팅 (12)
      • 과제 (3)
      • 쪽지시험 (2)
      • 정보처리기사 (4)
      • 서버 (25)
  • 블로그 메뉴

    • 링크

      • 포폴
      • 구깃
    • 공지사항

    • 인기 글

    • 태그

      macbook pro m4
      datalist
      file25
      spring-boot
      net2
      java_io1~10
      net5~10
      file24
      Oracle
      net4
      re_java10
      exam1_1~10
      net1
      net3
      notice_writer
      io_dto
      noticewriteok
      re2
      file25_t
      ab1
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    9na0
    [Servlet] 회원가입 만들기
    상단으로

    티스토리툴바