mvcPage Dynamic Web Project> member
cos.jar = 파일 업로드 처리를 위해
jackson~.jar = 데이터를 map형태로 넣고JSON문자열로 만들어주기 위해
jstl-1.2.jar = jstl태그를 사용하기 위해
ojdbc8.jar = DB연동을 하기 위해
src/main/webapp/sql/table.sql
백업용 sql파일을 만들고, SQL Developer 에서 TABLE과 SEQUENCE를 생성한다.
두 테이블을 JOIN해서 사용한다.
/* 회원등급 */
CREATE TABLE zmember(mem_num number not null, -- PK역할, 시퀀스를 이용하여 자동부여
id varchar2(12) unique not null, -- (재가입 방지) 탈퇴해도 정보를 남겨놓는다
auth number(1) default 2 not null, -- 회원 등급 > 0:탈퇴회원/1:정지회원/2:일반회원/3:관리자 입력을 안하면 기본값이 2(일반회원)
constraint zmember_pk primary key (mem_num) -- 오라클에서 사용하는 식별자
);
/* 상세정보(개인정보) */CREATE TABLE zmember_detail(
mem_num number not null,
name varchar2(30) not null,
passwd varchar2(12) not null,
phone varchar2(15) not null,
email varchar2(50) not null,
zipcode varchar2(5) not null,
address1 varchar2(90) not null,
address2 varchar2(90) not null,
photo varchar2(150), -- 처음에 사진 등록 안하니까 null값 인정되게
reg_date date default sysdate not null, -- 가입날짜 입력안해도 자동으로SYSDATE들어가게
modify_date date, --갱신날짜 갱신을 안하면 날짜가 안들어가니까 null값 인정
constraint zmember_detail_pk primary key(mem_num),
constraint zmember_detail_fk foreign key(mem_num) references zmemeber (mem_num)
);
-- (mem_num)을 참조하는데 [ references zmemeber (mem_num); ] zmember에있는 (mem_num)을 참조한다.
CREATE SEQUENCE zmember_seq;
- PRIMARY KEY : UNIQUE제약조건, NOT NULL제약조건이 같이 들어간다.
시퀀스 수가 너무 올라가서 1부터 실행하고 싶다면
DROP SEQUENCE sboard_seq; 실행 후에 CREATE SEQUENCE sboard_seq; 한다.
src/main/java/kr.member.vo(패키지)/MemberVO.java
컬럼명과 일치하는 VO(자바빈)를 만든다.
VO역할을 하는 클래스이므로 (컬럼명과 일치시켜서) private한 프로퍼티(≒멤버변수) 를 만들고.
이 때 private Date reg_date; 변수는 데이터베이스에 연동하기 때문에
import java.util.Date; 를 사용하는 것이 아니라, import java.sql.Date; 를 사용한다.
gertters, settters를 만들어준다.
생성된 get메서드/set메서드 와 프로퍼티 사이에 필요한 메서드를 생성할 수 있다.
로그인을 할 것이기 때문에 비밀번호의 일치여부를 체크하는 메서드를 만들어보자
//비밀번호 일치여부 체크
public boolean isCheckedPassword(String UserPasswd) {
//회원등급(auth) : 0:탈퇴회원, 1:정지회원, 2:일반회원, 3:관리자
//등급이 2, 3 인경우만 서비스 이용할 수 있도록
if(auth > 1 && passwd.equals(UserPasswd)) {
return true;
}
return false;
}
if(passwd.equals(userPasswd))
DB에 있는 비밀번호
사용자가 입력한 비밀번호
▼ code
src/main/java/kr.member.dao(패키지)/MemberDAO.java
싱글톤 패턴을 만들어준다. 싱글톤 패턴을 구현하기 위해서는
static한 변수를 만들어서 그걸 통해 객체가 생성되게 한다.
private static BoardDAO instance = new MemberDAO();
(instance) 가 최초 호출되면 객체( new MemberDAO(); )가 생성이 되는데 계속 호출되더라도 기존에 있는 객체를 계속 사용한다.
그렇게 재활용을 하기 위해서 외부에서 호출을 해야하니까 public한 메서드도 만든다.
public static BoardDAO getInstance() {
return instance ; }
생성자를 직접 호출 할 수 없게 private하게 만든다.(외부X, 내부에서만 호출할 수 있게)
private MemberDAO() {}
DAO 만들 때 가장 적합한 패턴 = 싱글톤 패턴(Singleton Pattern )
생성자를 외부에서 호출할 수 없도록 private으로 지정해서 처리하고
static 메서드를 호출해서 객체가 한 번만 생성되고 생성된 객체를 공유할 수 있도록 처리하는 방식을 의미한다.
* 메모리 관리 차원에서 효율적
∴ 멤버변수가 없는 경우(DAO)에는 메서드만 있기 때문에 객체를 하나만 만들고 재활용하여 서버에 부담을 줄이기 위해 싱글톤 패턴을 만들어서 활용을 한다.
JSP/SERVLET 이용 시 싱글톤 패턴을 사용한다.
VO에 있는 멤버변수는 매번 객체 생성해서 쓰는 반면에
DAO는 멤버변수를 가지고 있지 않고 메서드 단위로 동작을 한다. 그렇기 때문에 공유가 가능하다.
(객체를 여러개 만들면 부하가 걸리므로 계속 호출 되어도 하나의 객체만 반환하게 한다. → Singleton Pattern )
DAO에서는 커넥션 풀을 이용 할건데, 커넥션 풀을 사용하여 정보를 읽어오려면 설정 파일(context.xml)이 필요하다.
META-INF 에 context.xml파일을 넣어준다. [JSP] 🔗JDBC와 커넥션 풀
▼ context.xml파일
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jdbc/xe" → DAO에서 읽어올 명칭
auth="Container" → Container를 자원관리자로 기술
type="javax.sql.DataSource" → 반환 타입은 javax.sql.DataSource으로
username="scott"
password="tiger"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:xe"
maxTotal="20" → 톰캣이 제공하는 커넥션 풀에서 생성되는 최대 커넥션 숫자를 명시
maxIdle="10" /> → 데이터베이스 연동이 없더라도 커넥션 풀 유지를 위한 최대 대기커넥션 숫자
</Context>
이제, (JNDI 방식으로) context.xml의 정보를 읽어와서 커넥션 풀로부터 커넥션을 할당받아 작업을 해보자.
순서에맞게끔 데이터를 처리해준다
데이터베이스에 연동할 메서드들을 생성한다.
>일반 쪽
◎ID중복 체크 및 로그인처리
◎회원가입
◎회원상세정보
◎회원정보수정
◎비밀번호 수정
◎프로필 사진 수정
◎회원 탈퇴
>관리자 쪽
◎총 회원 수
◎회원 목록
◎회원 정보 수정
DAO가 여러개가 생성되니까 위에 코드를 넣으면 코드가 길어지므로
getConnection과 executeClose를 DBUtil.java 에 공유할 파일을 생성한다.
◎ ID중복 체크 및 로그인처리 checkMember
SQL문 작성
(zmemeber_detail테이블 : 삭제가 일어남)
(zmemeber 테이블 : 삭제안하고 보관만 )
→ zmember테이블과 zmemeber_detail테이블을 조인.
조인했을 때삭제하면 삭제한 데이터가 안보여진다. 아이디를 보관하는 이유가 중복가입방지인데 zmember의 누락된 데이터가 보여져야 id중복 체크 가능함
그래서 JOIN 시 아이디 누락되는 상황방지를 위해 OUTERJOIN을 햬준다.
sql = "SELECT * FROM zmember m LEFT OUTER JOIN zmember_detail d " //d뒤에 공백있음!
+ "ON m.mem_num=d.mem_num WHERE m.id=?";
id에 유니크제약조건을 줬으니까 하나의 레코드만 동작을한다
rs= pstmt.executeQuery();
if(rs.next()) {
반환을 다해왔으면 이제 모델클래스에서 checkMember() 를 호출할텐데,
모델클래스에서 json문자열을 만들 수 있게끔 정보를 담아야한다.
그 데이터를 request에 저장을 하고 그럼 최종적으로 JSP에서 JSON문자열이 만들어지고 그것이 전송된다.
기존에는 수작업으로 했다면
(중괄호로 만들어서 객체 형태로 명시해고 키와 밸류의 쌍으로 대괄호로 배열로 나열하고...)
이 라이브러리를 사용하면 값만 넘겨주면 JSON문자열을 만들어준다.
jackson라이브러리를 lib 경로에 넣어준다.
그리고 나서 모델클래스를 만든다. kr.member.action/CheckDuplicatedIdAction.java
▼code
◎ 회원가입 insertMember(MemberVO member)
SQL문을 3개 작성하는데 try문 안에서
하나는 시퀀스를 통해서 회원번호(mem_num)를 생성하는 것이고
두번째는 zmember테이블에 관한 것이고
세번째는 zmember_detail에 관한 것이다.
try {
SQL문장을 PreparedStatement객체가 각각 하나씩 처리 하기 때문에 pstmt도 3개를 생성한다.
PreparedStatement pstmt = null;
PreparedStatement pstmt2 = null;
PreparedStatement pstmt3 = null;
글 번호(mem_num)를 구하는 객체도 하나 명시한다. (시퀀스 번호를 저장한다.)
int num = 0;
커넥션풀로부터 커넥션을 할당받고,
SQL문장이 3개이므로 오토커밋auto commit 해제한다.
(SQL문장이 2개 이상이거나, 1개일 때도 헷갈리면 일관되게 해제하고 commit 문장을 넣어주며 사용할 수 있다.)
conn.setAutoCommit(false);
회원번호를 생성하는 첫번째 SQL문을 작성한다.
sql = "SELECT zmember_seq.nextval FROM dual";
* dual : ORACLE에서 제공해주는 가상테이블로,
테이블에 적용하지않는 데이터들을(날짜를 구한다거나, 연산을 한다거나) 독립적으로 동작시킬 때 dual 을 활용한다.
여기서는 시퀀스를 동작시켜서 값을 얻어내므로 dual을 활용할 수 있다. *
PreparedStatement객체 생성해서 SQL문을 넘겨주고 pstmt = conn.prepareStatement(sql);
SQL문을 테이블에 반영하여 결과행을 ResultSet에 담음 rs = pstmt.executeQuery();
이제 시퀀스가 담겨있으니까 하나의 레코드만을 읽어온다.
if(rs.next()) {
근데 결과 컬럼이 하나밖에 없기 때문에 zmember_seq.nextval을 넣기 보다 1이라고 컬럼인덱스로 명시한다.
num = rs.getInt(1); }
num에 시퀀스가 담겨있고 이것을 mem_num에 전달해준다.
zmember테이블에 데이터 저장하는 두번째 SQL문 작성
sql = "INSERT INTO zmember (mem_num,id) VALUES(?,?)"; (auth는 명시안하면 기본값(2)으로 들어감)
?에 데이터 바인딩해준다.
pstmt2.setInt(1, num);
pstmt2.setString(2, member.getId()); (VO에 담겨있음)
mem_num으로 JOIN하여 zmember_detail테이블에 데이터 저장하는 세번째 SQL문 작성
sql = "INSERT INTO zmember_detail(mem_num,name,passwd,phone,"
+ "email,zipcode,address1,address2) VALUES(?,?,?,?,?,?,?,?)";
PreparedStatement객체 생성, ?에 데이터 바인딩, SQL문 실행
SQL문 실행시 예외발생안하고 sql문 모두 실행 성공하면 commit 해준다.
conn.commit();
} catch (Exception e) {
SQL문이 하나라도 실패하면 rollback 후 예외를 던진다. (하나의 트랜잭션 단위에서 작업하기에 안하면 꼬인다.)
conn.rollback();
throw new Exception(e);
} finally {
pstmt가 3개이므로 자원정리를 세번 해준다.
DBUtil.executeClose(null, pstmt3, null);
DBUtil.executeClose(null, pstmt2, null);
DBUtil.executeClose(rs, pstmt, conn);
이제 RegisterUserAction 모델클래스를 생성한다
▼ code
◎ 회원 상세 정보 getMember(int mem_num)
테이블이 2개라서 조인해서 사용하기에 sql문장이 중요하다.
sql = "SELECT * FROM zmember m JOIN zmember_detail d " //d뒤에공백있음
+ "ON m.mem_num=d.mem_num WHERE m.mem_num=?";
SQL문장을 테이블에 반영한 후 결과 행을 구한다. (primary key가 전달되서 하나의 행만 접근할수 있게)
결과행을 ResultSet에 담는다.
rs = pstmt.executeQuery();
다명시하고 상세페이지에서 필요한 데이터를 꺼내는 형태로 작성할 수 있다.
/myPageAction 모델클래스를 생성하자.
▼ code
◎ 회원 정보 수정 updateMember(MemberVO member)
modify_date는 수정된 날짜로 SYSDATE를 이용하여 현재시간을 입력한다.
ModifyUserAction 모델 클래스를 작성하자.
▼ code
◎ 비밀번호 수정 updatePassword(String passwd, int mem_num)
zmember_detail 테이블에만 있는 비밀번호를 변경해준다.
ModifyPasswordAction 모델 클래스를 생성하자
▼ code
◎ 프로필 사진 수정 updateMyPhoto(String photo, int mem_num)
업데이트 개념으로 만들어준다. (비밀번호 수정 메서드와 같음) String photo 에는 파일명
UpdateMyPhotoAction 모델 클래스를 생성하자.
▼ code
◎ 회원 탈퇴(회원정보삭제) deleteMember(int mem_num)
위 회원가입 메서드의 설명을 참고.
SQL문이 2개 이므로 PreparedStatement객체를 2개 작성해야한다. 자원정리도 2번 해준다.
auto commit을 해제해주는거 잊지않고, 모든 SQL문이 실행성공하면 commit해주고 하나라도 실패하면 rollback해준다.
탈퇴회원을
zmember테이블에서는 삭제가 아니라 auth값을 변경해주고
zmember_detail테이블에서는 레코드를 삭제한다
▼ code
<관리자>
◎ 총 회원 수 getMemberCountByAdmin(String keyfield, String keyword)
검색할 때 필요한 빈 변수도 하나 설정해놓는다.(WHERE절을 담을 것이다.)
String sub_sql = "";
keyword가 null이 아니거나, 데이터가 있다면 빈문자열이 아닐 경우
if(keyword != null && !"".equals(keyword)) {
keyfield에 맞는 검색글 처리를 해준다. 검색을 안할경우 비어있음(String sub_sql = "";)
if(keyfield.equals("1")) sub_sql = "WHERE id LIKE ?";
else if(keyfield.equals("2")) sub_sql = "WHERE name LIKE ?";
else if(keyfield.equals("3")) sub_sql = "WHERE email LIKE ?"; }
JOIN형태로정보를 읽어오려는데 누락된 정보(탈퇴회원 정보)까지 가져오려고 한다.
(탈퇴회원은 개인정보가 없고 아이디와 auth값만 남아있도록 설정했기 때문에 두가지만 나온다.)
OUTER JOIN을 해준다. 같은컬럼에는 USING사용도 해준다.
sql = "SELECT COUNT(*) FROM zmember m " //m뒤에 공백있음
+ "LEFT OUTER JOIN zmember_detail d USING(mem_num)" + sub_sql;
//전체일경우는 sub_sql없이 들어가고 검색할경우는 위의 WHERE절이 들어간다
* 두테이블을 JOIN하는 방법말고 그냥 zmember테이블에서 COUNT해도 결과는 같음.
PreparedStatement객체를 생성하고
전체 레코드 수를 검색할 때는 '?' 가 없지만 검색할 때는 sub_sql의 '?'에 데이터를 바인딩 해야하니까 조건체크를 해준다.
keyword가 null이 아니거나, 데이터가 있다면 빈문자열이 아닐 경우
if(keyword != null && !"".equals(keyword)) {
pstmt.setString(1, "%" + keyword + "%"); }
데이터베이스에서 LIKE 검색을 하려면 가변문자 %s%를 앞뒤로 붙혀야 s(keyword)가 포함된 문자열 검색이 가능하다
▼ code
◎ 회원 목록 getListMemberByAdmin(int startRow, int endRow, String keyfield, String keyword)
전체 레코드 수를 검색할 때는 '?' 가 없지만 검색할 때는 sub_sql의 '?'에 데이터를 바인딩 해야하니까 조건체크를 해준다.
if(keyword!=null && !"".equals(keyword)) {
keyfield에 맞는 검색글 처리를 해준다. 검색을 안할경우 비어있음(String sub_sql = "";)
if(keyfield.equals("1")) sub_sql = "WHERE id LIKE ?";
else if(keyfield.equals("2")) sub_sql = "WHERE name LIKE ?";
else if(keyfield.equals("3")) sub_sql = "WHERE email LIKE ?"; }
reg_date를 기준으로 소팅하는데 탈퇴회원은 null이니까 NULLS LAST를 사용하여 NULL을 제일 뒤로 보내 작성한다.
sql = "SELECT * FROM (SELECT a.*, rownum rnum FROM(SELECT * FROM "
+ "zmember m LEFT OUTER JOIN zmember_detail d "
+ "USING(mem_num) " + sub_sql + "ORDER BY reg_date DESC NULLS LAST)a) "
+ "WHERE rnum >= ? AND rnum <= ?"; //회원목록과 총회원수의 카운트(개수)가 일치해야한다.
PreparedStatement객체를 생성하고
전체 레코드 수를 검색할 때는 '?' 가 없지만 검색할 때는 sub_sql의 '?'에 데이터를 바인딩 해야하니까 조건체크를 해준다.
if(keyword!=null && !"".equals(keyword)) {
검색을 안하고 전체 레코드 수를 검색할 때는 ?가 2개 이고
검색을 할 경우 ? 가 3개 이며, 중간에 ?가 끼어진다.("USING(mem_num) " + sub_sql + "ORDER BY reg_date DESC NULLS LAST)a) ")
중간에 검색하는 sub_sql의 ?에 바인딩하니까 1,2,3 번호가 겹치므로
에러방지 를 위해서 위에 변수(cnt)를 하나 지정하고,(int cnt = 0;) 데이터바인딩 시 활용한다.
if(keyword!=null && !"".equals(keyword)) {
//②pstmt.setString(1, "%" + keyword + "%");
pstmt.setString(++cnt, "%" + keyword + "%"); } cnt= 0 → cnt= 1
//①pstmt.setInt(1, startRow);
//①pstmt.setInt(2, endRow);
pstmt.setInt(++cnt, startRow); cnt= 1 → cnt= 2 / 조건체크 미적용시엔 cnt= 0 → cnt= 1
pstmt.setInt(++cnt, endRow); cnt= 2 → cnt= 3 / 조건체크 미적용시엔 cnt= 1 → cnt= 2
SQL문을 테이블에 반영하고 결과행들을 ResultSet에 담는다.
rs = pstmt.executeQuery();
넣어주고 한건의 레코드가 자바빈에 담기겠지만 그 자바빈이 여러개가 리스트에 담겨야하기 때문에 ArrayList를 생성해준다.
list = new ArrayList<MemberVO>();
데이터를 다 넣고 나중에 필요한 것만 뽑는 식으로 하기로 하자,
★데이터를 다 넣어서 들어갔으면 자바빈을 ArrayList에 저장한다. 꼭 해야한다. 안하면 비어있어야 호출이 안됌
list.add(member);
모델클래스에 페이지 처리해서 정보를 읽어오고 검색을 하는
MemberListAction.java
▼ code
◎ 회원 정보 수정 updateMemberByAdmin(MemberVO member)
auth값을 변경할 수 있어서 pstmt를 2개를 만든다.
▼ code
src/main/java/kr.util(패키지)/DBUtil.java
context.xml에서 설정정보를 읽어와서 커넥션 풀로부터 커넥션을 할당 받음
같은 클래스 내에서는 private으로 했었는데 공유되어지는 자원이므로 public으로 쓴다.
DAO가 늘어나더라도 이것들을 재활용해서 사용이 가능하다.
▼ code
src/main/webapp/WEB-INF/web.xml
servlet을 사용해야하는데 매핑을 걸어야한다. web.xml에서 매핑을 걸어주자.
<url-pattern>끝에 만들어질 주소를 명시하고
<servlet-class>그 주소가 호출하는 파일 경로를 상단부터 작성한다.
<init-param>를 작성하고 하위에
<param-name>식별자를 작성한다(이 식별자를 통해 정보를 읽어서 servlet에 전달을 한다.)
<param-value>설정파일 상단부터 작성한다. /WEB-INF/member.properties, /WEB-INF/board.properties
쉼표로 나열해서 여러개 작성도 가능하다.
member.properties 파일을 생성한다.
이 파일이 위에서 말했던 설정 파일이여서 클라이언트가 요청했을 때 어떤 모델클래스를 생성해야 할 지를 명시해준다.
그럼 그 모델클래스를 생성하여 사용한다.
▼ code
src/main/webapp/WEB-INF/member.properties
회원관리도하고 게시물관리도하고 ,, 서비스가 많아지니까 중간경로를 하나 만들어준다.
중간경로는 서비스에 따라서 작성해주면 된다. (main, member, board ... )
#main
/main/main.do=kr.main.action.MainAction 메인경로를 작성해준다
메인경로를 생성했으니 메인 모델을 생성해보자 MainAction.java (모델은 아직없고 경로에는 명시된 상태에서 실행하면 에러남)
jackson라이브러리가 만들어진 문자열을 잘 만들어주는지 테스트를 해보자
/member/checkId.do=kr.member.action.CheckDuplicatedIdAction 으로 명시하고 get방식으로 넘겨서 테스트해보자.
main/main.do 를 호출한다(index.jsp에서 실행)
member/checkId.do?id=dragon
으로 바꿔서 입력해보고 json문자열을 잘 반환하는지 확인한다.
이제 회원가입 폼 부분을 만들어보자
모델클래스에서 먼저 호출되어야하므로 모델클래스를 먼저 만들어본다. RegisterUserFormAction.java
/member/registerUserForm.do=kr.member.action.RegisterUserFormAction 을 명시하고 index.jsp에서 실행해서 회원가입을 눌러 구현한 Form이 연동이 잘되는지 확인한다.
회원가입 작업을하자 DAO로 간다.
/member/registerUser.do=kr.member.action.RegisterUserAction
회원가입을 해보고 아이디 중복확인과, 회원가입한 데이터가 sql developer에 잘 들어왔는지 확인한다.
RegisterUserForm으로 다시돌아가서 우편번호 서비스를 작성해주자
/member/loginForm.do=kr.member.action.LoginFormAction
작성하고 indext.jsp에서 테스트 하고 LoginAction 을 만들어준다.
/member/login.do=kr.member.action.LoginAction
LogoutAction 을 만들어준다
/member/logout.do=kr.member.action.LogoutAction
DAO의 회원상세정보 메서드를 작성한다
/member/myPage.do=kr.member.action.MyPageAction
/member/modifyUserForm.do=kr.member.action.ModifyUserFormAction
DAO의 회원정보수정 메서드를 작성한다
/member/modifyUser.do=kr.member.action.ModifyUserAction
ModifyPasswordFormAction을 만들어준다.
/member/modifyPasswordForm.do=kr.member.action.ModifyPasswordFormAction
비밀번호는 sql developer를 사용해서 확인할 수 있다.
/member/modifyPassword.do=kr.member.action.ModifyPasswordAction
FileUtil.java
/member/updateMyPhoto.do=kr.member.action.UpdateMyPhotoAction
/member/deleteUserForm.do=kr.member.action.DeleteUserFormAction
/member/deleteUser.do=kr.member.action.DeleteUserAction
/member/memberList.do=kr.member.action.MemberListAction
/member/detailUserForm.do=kr.member.action.DetailUserFormAction
/member/detailUser.do=kr.member.action.DetailUserAction
. . .
src/main/java/kr.main.action(패키지)/MainAction.java
동일한 형태의 모델을 만들기위해 Action을 implemets해주고 추상메서드 구현한다.
src/main/webapp/WEB-INF/views/main/main.jsp
css 링크 걸어주고
include 액션태그를 이용해서 메뉴가 반복되게끔 한다.
include 해줄 헤더가 필요하다. common/header.jsp 파일을 생성한다.
<jsp:include page="/WEB-INF/views/common/header.jsp"/>
▼ code
src/main/webapp/WEB-INF/views/common/header.jsp
<jsp:include>액션태그를 사용하여 페이지 모듈화를 한다.
그러려면 jstl이 필요하다.
include 가 되는 것이기 때문에 HTML 코드는 필요없기 때문에 지우고
jstl을 사용하기위해 taglib 를 넣어준다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
주석을 명시하고 그사이에 UI 작업을 한다. 로고 형태나 글자 형태나 상관없다.
<!-- header 시작 --> <!-- header 끝 -->
상황에 따라 메뉴를 다르게 보이게 하려고 한다.
EL이 가지고 있는 연산자 empty : null이거나 비어있는 경우 → true 반환
<c:if test="${!empty user_num && !empty user_photo}">
user_num이 있을 때 && user_photo가 있을 때}
user_num : Integer 객체
user_photo : String 객체
이기 때문에 empty 를 이용한다.
▼ code
src/main/webapp/images/ .jpg,png, ...
이미지를 넣어놓는다.
src/main/webapp/css/layout.css
각 상황별 css 셋팅을 해준다.
* 정사각형이 아니라 직사각형일 경우 원 안에 보여지게 할 중심이미지의 위치를 지정
object-position:top;
* 사각형의 모서리 둥근 정도를 지정하는 속성인데 50%를 지정하면 완전한 원이 됨
border-radius:50%
src/main/webapp/index.jsp
jsp실행, servlet연동실행 테스트를 해보자.
테스트 할 문자를 넣고 실행시켜보자.
매번 DispatcherServlet을 동작시켜서 *.do 라고 나오면 직접main.do로 바꾸는 것이 아니라 indext.jsp 를 호출한다.
그렇게 되면 대문페이지인 main.do를 보여준다.
<% response.sendRedirect(request.getContextPath()+"/main.do"); %>
Servlet을 진입해야해서 DispatcherServlet.java을 호출해야 하는데,
호출하면 화면이 xml에서 매핑이 되어있기 때문에 list.do가 아니라 *.do 를 찾는다
그것이 에러가 나서 직접 list.do로 고치는 작업을 수행했었는데,
편리하게 사용하기위해서 index.jsp에서 Redirect기법을 쓴 것이다.
이제 main 화면 작업이 끝났으니 회원가입, 그 중 먼저 ID중복체크를 해주자
dao로 가서 작성을 한다.
/main/java/kr.member.action(패키지)/CheckDuplicatedIdAction.java
Action 을 implement 해주고 추상메서드 명시하고
이 모델클래스에 ID가 전달된다. ID를 포스트 방법으로 전달받아서 인코딩 처리 후 데이터를 반환한다.
DAO의 checkMember(ID중복 체크 및 로그인처리 ) 에 넘겨준다.
MemberDAO dao = MemberDAO.getInstance();
MemberVO member = dao.checkMember(id);
데이터만 넘겨주면 jackson라이브러리가 문자열을 만들어준다. 그걸 VIEW에서 읽어 처리한다.
JSON문자열을 만들려면 key와 value의 쌍이 필요하므로 Map형태로 정보를 처리해서 문자열로 가공한다.
Map<String,String> mapAjax = new HashMap<String,String>();
(HashMap<String,String> mapAjax = new HashMap<String,String>(); 둘 다 써도 된다.)
HashMap 에 전달할 정보를 key와 value의 쌍으로 담아준다.
if(member == null) { //아이디 미중복
mapAjax.put("result","idNotFound");
}else { //아이디 중복
mapAjax.put("result","idDuplicated"); }
정보를 담았으면 JSON형식으로 변환을 해야한다.
①JSON형식으로 변환하기를 원하는 일반 문자열을 HashMap에 key와 value의 쌍으로 저장한 후
②jackson라이브러리가 가지고 있는 클래스인 objectMapper의 writeValueAsString메서드에 Map객체를 전달
③writeValueAsString메서드가 일반 문자열 데이터를 JSON형식의 데이터로 변환 후 반환
(jackson라이브러리를 lib에 넣으면 자동으로 패스가 걸린다.) objectMapper 객체 생성
ObjectMapper mapper = new ObjectMapper();
* import org.codehaus.jackson.map.ObjectMapper;
메서드에 Map을 연결해준다.
String ajaxData = mapper.writeValueAsString(mapAjax);
그러나 모델클래스는 전송하는 능력이 없기 때문에, request에 저장을 하고 JSP에서 가져가서 전송을 해야한다.
만들어진 데이터를 request에 저장
request.setAttribute("ajaxData", ajaxData);
"ajaxData" 라고 호출하면 문자열을 가져올 수있다. 그것을 읽어올 수 있는
JSP 경로 반환
return "/WEB-INF/views/common/ajax_view.jsp";
JSON문자열을 전송해주는 JSP는 공유가 가능하기에 common 경로에 넣는다.(받아서 전송만 하기 때문에)
▼ code
src/main/webapp/WEB-INF/views/common/ajax_view.jsp
JSON문자열을 만들어내기에 HTML태그는 필요없으므로 지운다.
JSON문자열은 XML 로 전송하는게 아니라 텍스트이며, 설정에 의해 만들어진 공백을 지운다
<%@ page language="java" contentType="text/plain; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="false"%>
이제 request에 저장된 데이터를 읽어와야하는데, request.setAttribute("ajaxData", ajaxData); 라고 저장했다
EL표기를 사용해서 읽어온다.
${ajaxData}
앞으로 이 파일이 JSON문자열을 만들어서 전송해주는 역할의 JSP이다.
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/java/kr.member.action(패키지)/RegisterUserFormAction.java : 회원가입폼 모델
여기는 데이터 셋팅이 없다.
return "/WEB-INF/views/member/registerUserForm.jsp";
▼ code
src/main/webapp/WEB-INF/views/member/registerUserForm.jsp : 회원가입JSP
CSS링크 넣어주고 UI작업을 해주는데 메뉴를 include작업을 해준다.
<jsp:include page="/WEB-INF/views/common/header.jsp"/>
제이쿼리가 필요하므로 폴더를 만들어 js파일을 넣어준다.
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-3.6.0.min.js"></script>
컨텍스트 경로부터 명시해야 에러 안남
유효성 체크도 해준다
아래가 아이디 중복체크의 기본적인 형태이다.
<script type="text/javascript">
$('#id_check').click(function(){
if($('#id').val().trim()==''){
alert('아이디를 입력하세요!');
$('#id').val('').focus();
return; //submit이 아니라 함수만 빠져나간다.
}
$.ajax({
url:'checkId.do',
type:'post', //아이디를 전송할 때
data:{id:$('#id').val()}, //id에 접근해서 키와 밸류의 쌍으로 보냄
dataType:'json',
cache:false,
timeout:30000, //캐시 인정안함
success:function(param){
},
error:function(){
}
});
}); //end of click
</script>
이제 member.properies설정파일에 경로를 지정해서 Form이 잘 호출되는 지 확인한다.
다음 우편번호 api를 참고해서 'iframe을 이용하여 레이어 띄우기'를 해준다.
참고항목 없이 상세주소로 쓰기에 약간 변형해서 사용한다.
DAO에 ID중복 체크 및 로그인처리(checkMember) 를 작성했었으니 loginFormAction 모델클래스를 만들어준다.
▼ code
src/main/java/kr.member.action(패키지)/RegisterUserAction.java : 회원가입 모델
전송된 데이터 인코딩 처리와 자바빈(VO)객체 생성하고 데이터를 넣어준다.
DAO와 연동해서
JSP 경로를 반환한다.
return "/WEB-INF/views/member/registerUser.jsp";
registerUser.jsp를 생성하자
▼ code
src/main/webapp/WEB-INF/views/member/registerUser.jsp : 회원가입 JSP
회원가입 완료한 화면을 구성해준다.
이제 member.properies설정파일에 경로를 지정해서 잘 호출되는 지 확인한다.
▼ code
src/main/java/kr.member.action(패키지)/LoginFormAction.java : 로그인폼 모델
셋팅할 정보가 없으니까 JSP경로만 반환한다.
return "/WEB-INF/views/member/loginForm.jsp"; 생성 해준다.
▼ code
src/main/webapp/WEB-INF/views/member/loginForm.jsp : 로그인폼 JSP
로그인폼 UI작업과 유효성체크작업을 해준다.
이제 member.properies설정파일에 경로를 지정해서 잘 호출되는 지 확인한다.
▼ code
src/main/java/kr.member.action(패키지)/LoginAction.java : 로그인 모델
전송된 데이터 인코딩 처리를 해주고 전송된 로그인데이터 반환을 해준다
String id = request.getParameter("id");
String passwd = request.getParameter("passwd");
아이디 중복체크를 위한 인증절차를 위해 DAO객체 생성 하고
MemberDAO dao = MemberDAO.getInstance();
MemberVO에 데이터를 담아온다.(id가 있다면)
MemberVO member = dao.checkMember(id);
id가없다면 null이 되겠다. 그것을 체크하기 위해 boolean변수 하나 생성한다.
boolean check = false;
조건체크를 해준다. 아이디가 존재한다면 비밀번호일치여부를 체크한다.
if(member!=null) {
check = member.isCheckedPassword(passwd);
아이디가 존재하므로 auth값(한건의 레코드값)을 가져올 수 있다. auth값을 저장한다.(로그인 실패시 auth체크용으로 저장한다. member=null일 경우에 auth도 null)
request.setAttribute("auth", member.getAuth()); //(0이면탈퇴회원, 1번이면정지회원, 3번이면 관리자)
* jsp에서는 session.setAttribute로 데이터 저장했지만
모델클래스에서는 request와 response 만 사용이 가능하고
session은 직접호출이 안됀다.(servlet에서도) 그렇기때문에 request(의 메서드를 통해)를 통해서 session을 구해야한다.
인증성공했다면 로그인처리를 해준다.
if(check) {
request.getSession();을 하게되면 session객체를 구할수 있다. * 우리가 사용했던 session의 타입이 HttpSession타입이다
HttpSession session = request.getSession();
session객체에 원하는 데이터를 넣어준다.
session.setAttribute("user_num", member.getMem_num());
session.setAttribute("user_id", member.getId());
session.setAttribute("user_auth", member.getAuth());
session.setAttribute("user_photo", member.getPhoto());
인증 성공 시 redirect형태로 이동한다. (문구를 굳이 안넣어도 ui로 확인할 수 있으므로 )
servlet에 들어있는 기능인데 redirect: 을 붙이면 그걸 식별해서 리다이렉트 방식으로 (/main/main.do) 클라이언트에게 호출을 시킨다.
return "redirect:/main/main.do"; }
인증 실패시 forward형식으로 호출한다.
return"/WEB-INF/views/member/login.jsp"; } 생성 해준다.
▼ code
src/main/webapp/WEB-INF/views/member/login.jsp : 로그인 JSP
조건체크를 하기위해 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 태그를 넣어준다.
<c:choose><c:when><c:otherwise> 사용해서 auth로 조건체크 한다.
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/java/kr.member.action(패키지)/LogoutAction.java : 로그아웃 모델
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
session 객체 생성하고 (request.getSession();을 하게되면 session객체를 구할수 있다.)
HttpSession session = request.getSession();
세션의 모든 속성을 제거하여 로그아웃처리
session.invalidate();
메인을 redirect하면 메인의 메뉴가 바뀌니까 굳이 문구를 안띄워도 사용자는 확인 할 수 있으므로,
리다이렉트 방식으로 /main/main.do호출
return "redirect:/main/main.do";
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/java/kr.member.action(패키지)/MyPageAction.java : 회원 상세정보 모델
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
HttpSession session = request.getSession();
로그인 여부를 확인한다. 로그인이 안된 경우엔 로그인폼으로 리다이렉트한다.
Integer user_num = (Integer)session.getAttribute("user_num"); [💁❓] - [Java] int? integer?
if(user_num == null) { //
return "redirect:/member/loginForm.do"; }
로그인이 된 경우엔 회원정보를 읽어온다
MemberDAO dao = MemberDAO.getInstance();
MemberVO member = dao.getMember(user_num);
"member"라는 속성명으로 memberVO에 저장
request.setAttribute("member", member);
return "/WEB-INF/views/member/myPage.jsp"; 를 생성하자
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/webapp/WEB-INF/views/member/myPage.jsp : 회원상세정보 JSP
연락처 부분을 보면 한 건의 레코드를 읽어오는 것인데 위의 주소를 보면 그냥/member/myPage.do라고 요청이 되있다. 전에는 한건의 레코드를 읽어올 때는 pk(num값)를 넘겨받아서 읽어왔는데
여기에서의 PK, mem_num(세션에서는 user_num)은 세션에 들어가 있기 때문에 세션에서 읽어오면 되므로 밖에 노출을 안시키는 것이다.
세션에서 읽어온다는 자체는 로그인 판별 후 읽어올 수 있기 때문에 회원번호를 겉으로 노출 시키지않는다.
css,js 링크를 넣어준다.UI작업을 하는데 <jsp:include page="/WEB-INF/views/common/header.jsp"/> 를 통해 메뉴불러오고프로필 사진쪽에 조건체크를 해야하므로 상단에 jstl태그도 넣어준다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
처음 화면이 로드됐을 때의 이미지가 기본이미지로 표시되게하려고 기본값을 넣어준다.
let photo_path = $('.my-photo').attr('scr');
이미지 파일객체를 담는 변수도 설정해준다.
let my_photo ;
이미지가 존재하는 경우와 그렇지않은 경우로 <c:if>태그를 이용해서 작성한다.(상용서비스 할 때는 작은 이미지와 큰이미지를 따로 설정해서 넣는다.)
프로필사진 수정 전송 취소회원정보 수정 탈퇴자바스크립트로 제어를 해준다.
//파일 전송
let form_data = new FormData();
FormData 인터페이스는 form 필드와 그 값을 나타내는 일련의 key/value 쌍을 쉽게 생성할 수 있는 방법을 제공합니다
파일 전송 시 FormData객체 생성해야하고, 아래 세가지 명시한다
▼ code
src/main/java/kr.member.action(패키지)/ModifyUserFormAction.java : 회원정보 수정폼 모델
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
request에 저장하고 jsp경로 반환
▼ code
src/main/webapp/WEB-INF/views/member/modifyUserForm.jsp : 회원정보 수정폼 JSP
form 태그 끝나는 코드 아래에 우편번호 api사용
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/java/kr.member.action(패키지)/ModifyUserAction.java : 회원정보 수정 모델
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
로그인 된 경우
전송된 데이터 인코딩 처리 후 자바빈 생성하여 데이터를 넣어준다.
DAO객체 생성 후 생성한 메서드로 데이터를 넣어주고
JSP경로반환
return "/WEB-INF/views/member/modifyUser.jsp"; 생성한다.
▼ code
src/main/webapp/WEB-INF/views/member/modifyUser.jsp : 회원정보 수정 JSP
데이터를 받기만하면 되니까 스크립트로만 작성해준다
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<script type="text/javascript">
alert('회원 정보를 수정했습니다');
location.href='myPage.do'
</script>
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/java/kr.member.action(패키지)/ModifyPasswordFormAction.java : 비밀번호 수정폼 모델
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
로그인 된 경우
받을 데이터 없으므로 바로 JSP경로반환
return "/WEB-INF/views/member/modifyPasswordForm.jsp"; 생성한다.
▼ code
src/main/webapp/WEB-INF/views/member/modifyPasswordForm.jsp : 비밀번호 수정폼JSP
새비밀 번호 확인까지 명시한 후 변심하여 새비밀번호를 수정하려고 하면 새비밀번호 확인을 초기화
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/java/kr.member.action(패키지)/ModifyPasswordAction.java : 비밀번호 수정 모델
DAO에서 비밀번호 수정메서드를 먼저 작업한다.
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
로그인 된 경우
전송된 데이터 인코딩 처리 후
전송된데이터를 반환받는다.
request.setCharacterEncoding("utf-8");
String id = request.getParameter("id");
현재 비밀번호와 새 비밀번호도 받는다.
String origin_passwd = request.getParameter("origin_passwd");
String passwd = request.getParameter("passwd");
현재 로그인한 아이디를 구한다.
String user_id = (String)session.getAttribute("user_id");
인증을 하기 위해 checkmember메서드를 활용한다 그러므로 dao호출하고
MemberDAO dao = MemberDAO.getInstance();
id가 있다면 한 건의 레코드를 읽어온다.
MemberVO member = dao.checkMember(id);
사용자가 입력한 아이디가 존재하며, 로그인한 아이디와 일치하는지를 체크하기위해 변수를 하나 지정한다.
if(member!=null && id.equals(user_id)) {
boolean check = false;
비밀번호 일치여부 체크
check = member.isCheckedPassword(origin_passwd);
인증성공의 경우에(true) 새 비밀번호와 회원번호를 DAO의 메서드에 넘겨주어 비밀번호를 변경한다.
if(check) { dao.updatePassword(passwd, user_num); }
* 비밀번호는 안보여지므로 제대로 수정이 됬는지 확인은 SQL Developer에서 확인한다.
UI처리를 위해서(정보가 잘못됬을 때 form을 호출하기 위해) 변수check값을 저장한다.
request.setAttribute("check", check);
JSP경로반환
return "/WEB-INF/views/member/modifyUser.jsp"; 생성한다.
▼ code
src/main/webapp/WEB-INF/views/member/modifyPassword.jsp : 비밀번호 수정JSP
저장한 check값이 true아니면 false로 간단하니까 HTML태그 다 지우고 제이쿼리를 이용해서 간단히 처리해보자.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 태그 넣어주고
<c:if test="${check }">
<script type="text/javascript">
alert('비밀번호 수정했습니다');
location.href='myPage.do';
</script>
</c:if>
<c:if test="${!check }">
<script type="text/javascript">
alert('아이디 또는 비밀번호 불일치');
history.go(-1);
</script>
</c:if>
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/webapp/upload/
유틸리티 형태로 만들어서 생성해놓는다
src/main/java/kr.util(패키지)/FileUtil.java : 유틸리티
cos 라이브러리를 사용해서 업로드 처리할 때는 이 만들어놓을 유틸리티를 사용할 것이다.
◎ 파일 업로드 Util
인코딩 타입을 지정한다.
public class FileUtil {
public static final String ENCODING_TYPE = "utf-8";
최대 업로드 사이즈를 지정한다.
public static final int MAX_SIZE = 5*1024*1024;
업로드 경로를 지정한다.
* 원래 절대경로를 입력해야하는데 절대경로를 구해서 입력해도 되고, context상의 절대경로가 복잡하므로(이클립스가 조작해서 만들어지기때문) 상대경로로 명시한다.
public static final String UPLOAD_PATH = "/upload";
cos 라이브러리를 사용해서 파일을 업로드하기위해 MultipartRequest 객체를 생성하고 createFile에 인자가 하나 있는데 거기에 request를 넘겨준다.
public static MultipartRequest createFile(HttpServletRequest request) throws Exception{
업로드절대경로를 구한다. 경로가 기므로 변수에 넣어줬다.
* Servlet에서는 ServletContext라고 부르고 = JSP에서는 Application이라고 부른다.
그 안에 getRealPath가 있는데 상대경로를 넣으면 절대경로를 구해주는 메서드이다.
String upload = request.getServletContext().getRealPath(UPLOAD_PATH );
생성했으니 바로 반환해준다.
return new MultipartRequest(request, upload, MAX_SIZE , ENCODING_TYPE , new DefaultFileRenamePolicy());
◎ 파일 삭제 Util
이번에도 static하게 명시를 한다. request와 DB에 등록된 file명을 구해온다.
public static void removeFile(HttpServletRequest request, String filename) {
fild명이 null일 경우와 아닐 경우의 조건체크를 하는데 (파일명이 null일 경우엔 무반응)
file명이 null이 아닐 때는 위에서 한 것처럼 업로드 상대경로를 넣어서 절대경로를 구한다.
if(filename!=null) {
String upload = request.getServletContext().getRealPath(UPLOAD_PATH );
파일객체를 생성한 후 경로정보를 가진 뒤(경로를 만들어 낸뒤) 삭제를 한다.
File file = new File(upload + "/" + filename);
그 경로가 오류가나서 경로가 존재하지 않을 때 조건체크를 하는데
존재 할경우 삭제
if(file.exists()) file.delete();
'파일명'같은 경우엔 DB에 보관이 되어야한다.
AJAX처리해서 upload개념으로 넣어준다
DAO의 프로필사진 수정 부분(updateMyPhoto)을 작성한다.
▼ code
src/main/java/kr.member.action(패키지)/UpdateMyPhotoAction.java
회원탈퇴 전 dao작성한다 다작성하면
동일한 형태를 만들어야하기 떄문에 implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하는데
기존 방식과 다르게 AJAX 통신을 하기 때문에
(예>로그인 30분 경과 후 통신시도하면 로그아웃되있다는 메세지를 클라이언트에 보낸다)
(기존엔 직접 클라이언트가 Servlet에 진입 후 모델클래스를 호출했다면
AJAX방식은 폼(화면)을 보여주는 형태가 아니라 데이터를 보내주는 형식이다.)
그래서 만들어지는 데이터를 담기 위해서 MAP객체를 생성한다.(key와 value의 쌍으로 저장하기 위해서)
생성한 객체 안에서 조건체크를 한다.
Map<String,String> mapAjax = new HashMap<String, String>();
* Map, Hashmap 둘다 import java.util.
HttpSession session = request.getSession();
"user_num"으로 로그인이 되어있는지 식별을 한다.
Integer user_num = (Integer)session.getAttribute("user_num");
ui가 아니라 데이터만 넘기기 떄문에 해시맵에 넣어서 준다.
if(user_num==null) {
mapAjax.put("result", "logout");
로그인이 된 경우에는
클라이언트에 정보를 보낼껀데 그 정보가 request에 담겨있기 때문에, 담겨있는 정보를 처리한다,.
update 개념이기 때문에 기존파일이 있다면 삭제를 해줘야한다.(기획상의 정책에 따라 안지우는 경우도 있긴하다.)
먼저 기존정보가 있을 수 있기 때문에 기존의 이미지 파일 정보를 읽어온다.
MemberDAO dao = MemberDAO.getInstance();
MemberVO db_member = dao.getMember(user_num);
기존 데이터를 불러왔느데 null이다 → FileUtil에서 만약 파일명이 null 이면 무반응이도록 만들어놨으니, null이 아닐경우에만 이 작업을 하게된다.
클라이언트가 데이터를 보냈기 떄문에 전송된 파일 로드 처리 작업을 한다.
MultipartRequest 타입으로 반환을 해주면 되는데 FileUtil을 만들었으니까 그 유틸리티를 호출해주면 된다.
호출하고 request를 넣어준다.
MultipartRequest multi =FileUtil.createFile(request);
* MultipartRequest는 import
결국 FileUtil의 createFile만 동작하면 절대 경로의 파일을 업로드 한 셈이다.
업로드된 파일의 파일명을 구한다. (서버에 저장된 파일명 반환) 파라미터 네임은 "photo"로 한다.
String photo = multi.getFilesystemName("photo");
프로필을 수정한다 (파일명,회원번호(mem_num))
dao.updateMyPhoto(photo, user_num);
로그인 할 떄 session에 user_photo라는 이름으로 포토정보를 저장해놨는데 (LoginAction.java : 로그인 모델)
갱신작업을 하면 DB에 있는 포토이름과 session에있는 포토이름이 달라질 것이다.
그런 현상 때문에 수정을 했으면 session에 저장된 프로필사진 정보를 갱신해줘야한다.
session.setAttribute("user_photo", photo);
갱신을 하고 이전 프로필 이미지를 삭제한다 .(db_member.getPhoto()가 null이 아니면 삭제 작업을 할 것이다.)
FileUtil.removeFile(request, db_member.getPhoto());
정상적으로 처리 됐으면 map에 put한다.
mapAjax.put("result","success");
JSON 데이터로 변환하는데 ObjectmMapper객체를 생성한다.
ObjectMapper mapper = new ObjectMapper();
String ajaxData = mapper.writeValueAsString(mapAjax);
만들어진 데이터를 jsp에 전송해야하니까 request에 전송하고
(이때 식별자 역할을 해야하는 ajaxData는 문자열이여야하니 큰따옴표 붙여서 "ajaxData"로 명시)
request.setAttribute("ajaxData", ajaxData);
JSP경로를 반환한다.
return "/WEB-INF/views/common/ajax_view.jsp";
myPage.do(mypage.jsp)를 호출했을 때 그곳에서 AJAX통신을 한다.
mypage.jsp로 가보자
▼ code
src/main/java/kr.member.action(패키지)/DeleteUserFormAction.java : 회원탈퇴 폼 모델
먼저 dao의 회윈탈퇴 메서드를 작성한다
반복하고 있는 기본적인 로그인 여부만 체크하면 된다.
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
HttpSession session = request.getSession();
로그인 여부를 확인한다. 로그인이 안된 경우엔 로그인폼으로 리다이렉트한다.
Integer user_num = (Integer)session.getAttribute("user_num");
if(user_num == null) { return "redirect:/member/loginForm.do"; }
return "/WEB-INF/views/member/deleteUserForm.jsp"; }
▼ code
src/main/webapp/WEB-INF/views/member/deleteUserForm.jsp : 회원탈퇴 폼 JSP
<link rel="stylesheet" href="${pageContext.request.contextPath }/css/layout.css">
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-3.6.0.min.js"></script>
기본적인 유효성 체크와
비밀번호 확인까지 한 후 다시 비밀번호를 수정하면 비밀번호 확인 및 메시지 초기화
비밀번호와 비밀번호의확인 일치 여부 체크
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/java/kr.member.action(패키지)/DeleteUserAction.java : 회원탈퇴 모델
먼저 dao의 회윈탈퇴 메서드를 작성한다
반복하고 있는 기본적인 로그인 여부만 체크하면 된다.
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
HttpSession session = request.getSession();
로그인 여부를 확인한다. 로그인이 안된 경우엔 로그인폼으로 리다이렉트한다.
Integer user_num = (Integer)session.getAttribute("user_num");
if(user_num == null) { return "redirect:/member/loginForm.do"; }
----------------------------------------------------------------
비밀번호 수정 모델(ModifyPasswordAction.java)과 인증 성공후 만 다르고 전에는 전부 비슷하다
인증성공의 경우에(true) DAO의 메서드를 통해 회원정보를 삭제한다.
if(check) { dao.deleteMember(user_num);
프로필도 삭제한다 안하면 쓰레기로 남기때문이다.
FileUtil.removeFile(request, db_member.getPhoto());
로그아웃 처리 까지 해준다.
session.invalidate(); }
UI처리를 위해서(정보가 잘못됬을 때 form을 호출하기 위해) 변수check값을 저장한다.
request.setAttribute("check", check);
JSP경로반환
return "/WEB-INF/views/member/deleteUser.jsp"; 생성한다.
▼ code
src/main/webapp/WEB-INF/views/member/deleteUser.jsp : 회원탈퇴 JSP
HTML태그를 지우고 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:if test="${check }"><c:if>
<c:if test="${!check }"><c:if>
이용해서 UI작성
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
일반 사용자에 관한 작업을 완료하고 관리자에 관한 작업을 수행한다.
admin으로 회원가입 후 SQL developer에서 auth값을 3으로 변경하고 commit 까지 해준다.
총 회원수 DAO 작업을 한다.
src/main/java/kr.member.action(패키지)/MemberListAction.java : <관리자>회원목록 모델
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
HttpSession session = request.getSession();
로그인 여부를 확인한다. 로그인이 안된 경우엔 로그인폼으로 리다이렉트한다.
Integer user_num = (Integer)session.getAttribute("user_num");
if(user_num == null) { return "redirect:/member/loginForm.do"; }
------------------------ 여기까진 기존 폼과 똑같음 ------------------------
MemberListAction에서는 조건체크가 하나 더 있다.
일반회원은 여기까지하면 로그인이 가능하지만 관리자는 auth값으로 조건체크를 한번 더 해준다.
Integer user_auth = (Integer)session.getAttribute("user_auth");
if(user_auth < 3) { //관리자로 로그인 하지않은경우
return "/WEB-INF/views/common/notice.jsp"; } //잘못된 접속을 알려주는 다양한 방법이 있지만 여기서는 문구로 처리해보자.
- notice.jsp를 만들어준다.
관리자로 로그인 한 경우 페이지처리를 해야하기 때문에 getParameter에서 "pageNum"을 받는다.
String pageNum = request.getParameter("pageNum");
if(pageNum == null) pageNum = "1";
검색관련 처리를 하는데 검색할 때 get방식을 이용한다.
최초 호출할때는 keyfield와 keyword가 없으니까 그럴 경우에 비어있도록 처리해놓는다.
String keyfield = request.getParameter("keyfield");
String keyword = request.getParameter("keyword");
if(keyfield == null) keyfield = "";
if(keyword == null) keyword = "";
DAO객체를 생성하여 count를 불러오고, getMemberCountByAdmin메서드에 keyfield와keyword를 넘겨준다.
MemberDAO dao = MemberDAO.getInstance();
int count = dao.getMemberCountByAdmin(keyfield, keyword);
페이지처리를 하려면 PagingUtil에 데이터를 넘겨줘야하는데 검색을 할 경우 인자의 순서 배치가 아래와 같다.
PagingUtil객체를 생성하고 인자를 순서대로 넣어주면 된다.
PagingUtil page = new PagingUtil(keyfield, keyword, Integer.parseInt(pageNum),count, 20, 10, "memberList.do");
List를 읽어오고 데이터가 있을 경우에 호출한다.
List<MemberVO> list = null;
if(count >0) { list = dao.getListMemberByAdmin(page.getStartCount(),page.getEndCount(),keyfield,keyword); }
만들어진 데이터가 count, list, pagingHtml 세가지인데 이 데이터를 넘겨준다
request.setAttribute("count", count);
request.setAttribute("list", list);
request.setAttribute("pagingHtml", page.getPagingHtml());
return "/WEB-INF/views/member/memberList.jsp";
/memberList.jsp 생성
▼ code
src/main/webapp/WEB-INF/views/common/notice.jsp : 안내
스타일을 그대로 유지하기위해 css링크를 넣어주고
조건을 사용해서 안내문구를 보여줘야 하는경우가 있느데 조건을 이용해서 다양한 처리를 하기위해 jstl태그를 넣어준다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
안내 창에는 메뉴가 없는경우가 꽤 많다 (기획상의 문제)
필요한 문구가 있다면 문구를 전달할 수 있는데 forward방식으로 호출하게 할 것이기 때문에 request를 공유할 수있다.
notice.jsp를 forward하기 전에 request에서 데이터를 넣어놓고 그 것을 읽어올 수가 있다.
그래서 조건체크를 해보자면
accessMsg 속성명이 비어있지 않으면(=있으면), 속성명을 사용한다(=속성명을 통해 데이터를 읽어오기).
<c:if test="${!empty accessMsg }">
${accessMsg }
</c:if>
반대로 accessMsg 속성명이 비어있으면(=문구 셋팅을 안했으면)
<c:if test="${empty accessMsg }">
잘못된 접속입니다.
</c:if>
이동하는 주소를 셋팅할 수있는데 이동하는 주소가 있으면 그 주소를 사용하고, 없으면 기본주소를 쓴다.
<c:if test="${!empty accessUrl }">
<input type="button" value="이동" onclick="location.href='${accessUrl }'">
</c:if>
<c:if test="${empty accessUrl }">
<input type="button" value="홈으로" onclick="location.href='${pageContext.request.contextPath}/main/main.do'">
</c:if>
지금 accessMsg와accessUrl 셋팅을 안했기 때문에 기본값이 나올 것이다.
→ (잘못된 접속입니다 , /main/main.do 로 이동)
▼ code
src/main/webapp/WEB-INF/views/member/memberList.jsp : <관리자>회원목록 JSP
검색 폼이 있기 때문에 CSS link와 JS script를 넣어준다.
count조건체크도 하기 때문에 jstl태그도 넣어준다.
검색할 때는 일반적으로 get방식으로 처리한다.
<form action="memberList.do" method="get" id="search_form">
keyfield를 value로 나눠준다.
UI작업을 하고 count로 조건체크를 해준다.
list가 저장되있기 때문에(${list }) "member"라고 읽어온 후 roop를 돌면서 보여지는데
<c:forEach var="member" items="${list }">
탈퇴회원의 경우 정보가 없다. 그래서 정보가 있는 사람들만 수정할 수 있도록 auth값을 체크한다.
<c:if test="${member.auth > 0 }">
<a href="detailUserForm.do?mem_num=${member.mem_num }">${member.id }</a>
</c:if>
<c:if test="${member.auth == 0 }">
<a>${member.id }</a>
</c:if>
layout.css 에 css작업도 해준다.
keyfield, keyword데이터는 MemberListAction 폼에서 받아서 null이 아닐경우 getMemberCountByAdmin메서드를 실행
이제 member.properies설정파일에 경로를 지정해준다.
DAO.회원정보 수정작업을 해보자
▼ code
src/main/java/kr.member.action(패키지)/DetailUserFormAction.java : <관리자>회원정보수정 폼 모델
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
HttpSession session = request.getSession();
로그인 여부를 확인한다. 로그인이 안된 경우엔 로그인폼으로 리다이렉트한다.
Integer user_num = (Integer)session.getAttribute("user_num");
if(user_num == null) {
return "redirect:/member/loginForm.do"; }
------------------------여기까진 회원상세폼과 똑같음( MyPageAction.java )--------------
detailuserform는 조건체크가 하나 더 있다. 관리자로 로그인 한 경우와 아닌 경우
Integer user_auth = (Integer)session.getAttribute("user_auth");
if(user_auth < 3) { //관리자로 로그인 하지않은경우
return "/WEB-INF/views/common/notice.jsp"; }
관리자로 로그인 한 경우 get방식으로 회원번호를 받기때문에
int mem_num = Integer.parseInt(request.getParameter("mem_num"));
한건의 레코드를 읽어와야하는데 한건의 레코드를 읽어올수 있도록 dao에 메서드를 만들어놨다
MemberDAO dao = MemberDAO.getInstance();
MemberVO member = dao.getMember(mem_num);
한 건의 레코드를 구해왔으니 "member"라는 속성명으로 memberVO에 저장 후 JSP 경로를 반환해준다.
request.setAttribute("member", member);
return "/WEB-INF/views/member/detailUserForm.jsp"; 를 생성하자
▼ code
src/main/webapp/WEB-INF/views/member/detailUserForm.jsp : <관리자>회원정보수정 폼 jsp
registerUserForm.jsp에서 사용했던 우편번호 API추가
등급에 맞게 radio버튼이 check 되어지도록 조건체크 해준다.
<li>
<label>등급</label>
<c:if test="${member.auth !=3 }">
<input type="radio" name="auth" value="1" id="auth1" <c:if test="${member.auth == 1}">checked</c:if> >정지
<input type="radio" name="auth" value="2" id="auth2" <c:if test="${member.auth == 2}">checked</c:if> >일반회원
</c:if>
<c:if test="${member.auth == 3 }"><%--관리자는 등급변경 못하게 checked --%>
<input type="radio" name="auth" value="3" id="auth3" checked="checked">관리자
</c:if>
</li>
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
src/main/java/kr.member.action(패키지)/DetailUserAction.java : <관리자>회원정보수정 모델
implements Action, 추상메서드 구현하고
회원제 서비스이기 때문에 로그인을 한 다음 진입을 해야하므로 조건체크를 해준다.
HttpSession session = request.getSession();
로그인 여부를 확인한다. 로그인이 안된 경우엔 로그인폼으로 리다이렉트한다.
Integer user_num = (Integer)session.getAttribute("user_num");
if(user_num == null) {
return "redirect:/member/loginForm.do"; }
detailuserform는 조건체크가 하나 더 있다. 관리자로 로그인 한 경우와 아닌 경우
Integer user_auth = (Integer)session.getAttribute("user_auth");
if(user_auth < 3) { //관리자로 로그인 하지않은경우
return "/WEB-INF/views/common/notice.jsp"; }
관리자로 로그인 한 경우 전송된 데이터를 인코딩 처리 해준다.
int mem_num = Integer.parseInt(request.getParameter("mem_num"));
request.setCharacterEncoding("utf-8");
vo에 데이터들을 넘겨주고
회원정보수정메서드(updateMemberByAdmin)에 정보를 넘겨준다
MemberDAO dao = MemberDAO.getInstance();
dao.updateMemberByAdmin(member);
return "/WEB-INF/views/member/detailUser.jsp"; 를 생성하자
▼ code
src/main/webapp/WEB-INF/views/member/detailUser.jsp : <관리자>회원정보수정 jsp
스크립트처리를 할 것이기 때문에 HTML태그를 다지운 후 JS코드 작성을 한다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<script type="text/javascript">
alert('회원정보 수정이 완료되었습니다');
location.href='memberList.do';
</script>
이제 member.properies설정파일에 경로를 지정해준다.
▼ code
'WEB > ✿JSP' 카테고리의 다른 글
[JSP] 🔗/MVC (3) 실습 (0) | 2022.01.25 |
---|---|
[JSP] 🔗/MVC (2) 실습 (0) | 2022.01.25 |
[JSP] 🔗MVC (0) | 2022.01.20 |
[JSP] 🔗/EL & JSTL (0) | 2022.01.20 |
[JSP] 🔗모델1 구조(3) 실습 (0) | 2022.01.18 |
댓글