(책) 자바 웹을 다루는 기술

Chap 17 모델2 방식으로 효율적으로 개발하기 -1

EunaSon 2023. 9. 9. 13:06

17.1 웹 애플리케이션 모델                 

17.2 MVC 디자인 패턴                        

17.3 MVC를 이용한 회원관리             

17.4 모델2로 답변형 게시판 구현하기


17.1 웹 애플리케이션 모델

666p

지금의 웹 애플리케이션 개발은 일반적으로 많이 사용하는 표준화 소스 구조를 만들어 개발을 진행함. 이러한 표준화된 소스 구조를 웹 애플리케이션 모델이라고 함. 웹 애플리케이션 모델에는 모델1과 모델2 방식이 있음.

 

모델1 방식

여태까지 jsp 실습 예제를 구현한 방식이 모델1 방식임. 비즈니스 로직 작업과 그 작업결과를 나타내주는 작업을 동일한 JSP에서 수행함. 즉, 모든 클라이언트의 요청과 비즈니스 로직 처리를 JSP가 담당하는 구조임.

브라우저 ↔ [ JSP - 화면기능 로직 처리  ↔ DAO 클래스 ] ↔ 데이터베이스 

 

기능 구현이 쉽고 편리하다는 장점

but 화면 기능과 비즈니스 로직 기능이 섞이므로 코드 재사용성이 저하되고  기능이 조금만 복잡해져도 유지보수가 어렵다는 단점

=> 모델1의 이런 단점을 보완한 것이 모델2 방식임

 

 

모델2 방식

웹 애플리케이션의 각 기능(클라이언트의 요청 처리, 응답 처리, 비즈니스 로직 처리)를 분리해서 구현하는 것이 핵심. 객체 지향 프로그래밍에서 각각의 기능을 모듈화해서 개발하자는 것과 같은 원리임.

                ↗ [ 요청 처리 ↔ 로직 처리 ] ↔ 데이터베이스

브라우저      [        ↕                            ] 

                ↖ [ 화면 기능                     ]

 

전체 기능을 몰라도 각 기능이 모듈화되어 있으므로 자신이 맡은 부분만 개발하면 됨.

각 부분을 조립하면 나중에 전체를 사용할 수 있으므로 개발 효율성이 높고, 유지 보수가 편하며 비슷한 프로그램을 만들 때 개발한 모듈들을 다시 사용할 수 있어 코드 재사용성이 높음

 

- 모델2 방식의 특징

각 기능이 서로 분리되어 있어 개발 및 유지보수가 쉬움

각 기능(모듈)의 재사용성이 높음

디자이너와 개발자의 작업을 분업화해서 쉽게 개발할 수 있음

모델2 방식과 관련된 기능이나 개념의 학습이 필요함

 

 17.2 MVC 디자인 패턴

669p

모델2에서 가장 자주 사용되는 개념인 MVC는 Model-View-Controller의 약자로, 일반 pc 프로그램 개발에 사용되는 디자인 패턴을 웹 애플리케이션에 도입한 것임. 즉, 웹 애플리케이션을 화면 부분, 요청 처리 부분, 로직 처리 부분으로 나누어 개발하는 방법.

모델2 방식으로 구현한다 = MVC로 구현한다와 같은 의미

 

                 [ 요청 처리(Controller) ↔ 로직 처리(Model) ] ↔ 데이터베이스

브라우저      [        ↕                                                             ] 

                 [ 화면 기능(View)                                            ]

 

Controller : 사용자로부터 요청을 받아 어떤 비즈니스 로직을 처리해야 할지 제어함

Model : DB 연동 등 비즈니스 로직을 처리함

View : 모델에서 처리한 결과를 화면에 구현하여 클라이언트로 전송함

 

 

- MVC의 특징

각 기능이 분리되어 있어 개발 및 유지보수가 편리함

각 기능의 재사용성이 높아짐

디자이너와 개발자의 작업을 분업화해서 쉽게 개발할 수 있음

 

MVC 구성 요소와 기능

Controller - 서블릿

서블릿이 컨트롤러의 역할을 함

클라이언트의 요청을 분석해서 요청에 대해 필요한 Model을 호출함

Model에서 처리한 결과를 보여주기 위한 JSP를 선택함

 

Model - DAO, VO 클래스

DB 연동과 같은 비즈니스 로직을 수행함

일반적으로 DAO와 VO 클래스로 이루어져 있음

 

View - JSP

JSP가 화면 기능을 담당함

Model에서 처리한 결과를 화면에 표시함

 

17.3 MVC를 이용한 회원 관리

671p

14장에서 회원관리 기능 구현한 적이 있음

( 14.12 (600p)에서 회원관리 실습 했었음(memberForm.jsp(회원가입창), member_action.jsp(DB 연동 기능), memberList.jsp(회원 정보를 화면에 표시함), MemberBean 클래스, MemberDAO 클래스 이용) )

 

회원 정보 조회 기능 구현

1) 브라우저 -> MemberController : /mem.do로 요청함

2) MemberController -> MemberDAO : 서블릿 MemberController가 요청을 받아 MemberDAO의 llistMembers() 호출

3) MemberDAO - > DB : MemberDAO의 listMembers() 메서드에서 SQL문으로 회원정보를 조회한 후 회원 정보를     MemberVO에 설정, MemberController에 반환함

4) MemberController -> listMembers.jsp : 조회한 회원 정보를 회원 목록창으로 포워딩함

5) listMembers.jsp -> 브라우저 : 포워딩한 회원 정보를 목록으로 출력해줌

 

672p

새 프로젝트 pro17에 sec01.ex01 패키지 생성 후 MemberController, MemberDAO, MemberVO 클래스를 추가함

WebContent에 test01 폴더를 만들고 listMembers.jsp를 추가함

 

[ MemberController 클래스 ]

init() 메서드에서 MemberDAO 객체를 초기화함

doHandle()에서 MemberDAO의 listMembers() 메서드 호출해서 회원정보를 ArrayList로 반환받음

조회한 회원 정보를 request에 속성이름 membersList 으로 바인딩함

RequestDispatcher 클래스를 이용해서 회원 목록창(listMembers.jsp)으로 포워딩함

 

@WebServlet("/mem.do")

...

MemberDAO memberDAO;

 

...init()

  memberDAO = new MemberDAO(); // MemberDAO를 생성

 

...doHandle()

  List membersList = memberDAO.listMembers(); // 회원정보 목록을 조회함

  request.setAttribute("membersList", membersList); // 조회한 목록 리스트를 request에 바인딩함

  RequestDispatcher dispatch = request.getRequestDispatcher("/test01/listMembers.jsp");

  dispatch.forward(request, response); // listMembers.jsp로 포워딩함

 


*** serialVersionUID 의 용도

serialVersionUID는 직렬화(implements serializable)를 할 때 사용함

직렬화 : 자바 객체를 바이트 배열로 변환해서 파일, 메모리, DB 등이 저장하는 과정

역직렬화 : 저장된 것을 다시 바이트 배열로 변환 후 객체로 변하는 과정

직렬화와 역직렬화

직렬화 할때는 serialVersionUID도 저장함, 역직렬화해서 객체로 다시 불러들일 때는 그 값을 체크해서 다르면 예외를 발생시킴(InvalidClassException)

serailVersionUID 값은 파일 등으로 저장할 때 해당하는 클래스의 버전이 맞는지를 확인하는 중요한 장치인데

값을 지정하지 않으면 Java VM에서 내부 알고리즘에 따라서 자동으로 지정됨, 이는 어떤 Java VM을 사용하는지에 따라 값이 달라지게 되므로 역직렬화 할 때 예외가 발생할 수 있음

따라서 serialVersionUID 값을 설정하는 것이 권장됨

 

출쳐 https://blog.naver.com/kkson50/220564273220


 

 

[ MemberDAO 클래스 ]

listMembers() 메서드 호출 시 SQL문을 이용해 회원 정보를 조회한 후 결과를 ArrayList로 반환함

 

public class MemberDAO

{

private DataSource dataFactory;

private Connection conn; 

private PreparedStatement pstmt;

 

public MemberDAO() // 생성자

{ // try-catch

Context ctx = new InitialContext();

Context envContext = (Context) ctx.lookup("java:/comp/env");

dataFactory = (DataSource) envContext.lookup("jdbc/oracle");

 

public List listMembers()  // 전체 회원목록 조회 메서드

{

List membersList = new ArrayList();

 

//try-catch문

conn = dataFactory.getConnection();

String query = "select * from t_member order by joinDate desc";

System.out.rintln(query); // 쿼리 콘솔에 출력

 

pstmt = conn.prepareStatement(query); // PrepareStatement 객체를 생성하면서 쿼리를 인자로 전달함

ResultSet rs = pstmt.executeQuery();

while 문으로 rs에서 getString(), getDate() 등으로 id, pwd, name, email, joinDate를 가져와 변수에 저장, MemberVO 생성자에 변수 다섯가지를 줘서 VO 객체 생성, membersList에 memberVO 객체 add()함 

 

rs, pstmt, conn을 close()하여 자원을 해제함

 

...

return membersList;

}

 

public void addMember(MemberVO m)  // 테이블에 회원 추가

{

//try-catch문

conn = dataFactory.getConnection();

getter로 VO객체인 인자 m에서 id, pwd, name, email 가져와 변수에 저장,

String query = "INSERT INTO t_member(id, pwd, name, email)" + "VALUES(?, ?, ?, ?)";

콘솔창에 쿼리 출력,

pastmt = conn.preparedStatement(query);

pstmt.setString()으로 1,2,3,4 물음표에 id, pwd, name, email 변수를 전달

pstmt.executeUpdate(); // select문이 아닌 insert, update 문 등은 executeQuery()가 아닌 executeUpdate()로 실행함

 

pstmt, conn을 close()하여 하원 해제

}

 

 

[ MemberVO 클래스 ]

멤버변수로 id, pwd, name, email, joinDate를 선언,
기본생성자, 인자 4개 갖는 생성자(joinDate 빠진거), 인자 5개 갖는 생성자(joinDate 까지 포함된거)와

각 멤버변수에 대한 geter/setter를 생성

 

 

[ listMembers.jsp ]

바인딩된 회원 정보를 차례대로 표시함

 

페이지 디렉티브 태그,

taglib 디렉티브 태그 설정

...

table 태그를 이용해서 회원 정보 출력함

아래에 a 태그로 '회원가입하기' 링크를 표시함

 

 

 회원 정보 추가 기능 구현

678p

이번에는 컨트롤러에서 회원 정보 조회뿐 아니라 회원 정보 등록까지 구현해보자.

앞에서보다 브라우저로부터 전달되는 요청사항이 많아졌기 때문에 컨트롤러는 브라우저로부터 어떤 요청을 받았는지 알아내야 함. 그 다음 요청을 처리할 수 있는 모델을 선택하여 작업을 요청해야 함. 이 역할을 하는 방법을 커맨드(command) 패턴이라 함.

 

커맨드 패턴

: 브라우저가 URL 패턴을 이용컨트롤러에게 수행 작업을 요청하는 방법임. 

컨트롤러는 getPathInfo() 메서드를 이용해 URL 패턴에서 요청명을 받아와 작업을 수행함

 

요청 URL을 이용해 컨트롤러에 요청하는 형식

http://localhost:8090/pro17/member/listMembers.do

1. /member : 첫 번째 단계의 요청 : 회원 기능

2. /listMembers.do : 두 번째 단계의 요청 : 회원 기능 중 회원 조회 기능

 

 

회원 정보 등록(회원 가입) 기능 흐름

1. 회원가입창(memberForm.jsp)에서 회원정보를 입력하고 URL 패턴을 /member/addMember.do 로 서버에 요청함

2. MemberController에서 getPathInfo() 메서드를 이용해 요청명인 /addMember.do를 받아옴

   -> 요청명에 따라 MemberDAO의 addMember() 메서드를 호출함

3. addMember() 메서드에서 SQL문으로 테이블에 회원 정보를 추가함

 

 

sec02.ex01 패키지를 만들고 MemberDAO와 MemberVO클래스를 복붙함

text01 폴더의 listMembers.jsp(회원목록 table 태그로 출력함)도 복붙함

 

[ MemberController 클래스 ] - 수정

getPathInfo() 메서드를 이용해 요청을 가져옴

if문을 이용해서 action값이 null 이거나 /listMembers.do 이면 회원 조회 기능을,

action 값이 /memberForm.do 이면 회원 가입 창을 나타내고

action 값이 /addMember.do 이면 전송된 회원 정보들을 테이블에 추가함

 

@WebServlet(" /member/* ") // 브라우저에서 요청이 두단계로 이루어짐 /member -> /~~~

변수 String nextPage = null; 로 선언 및 기본값으로 초기화함, 포워딩할 페이지를 저장할 변수임

String action = request.getPathInfo(); 로 URL에서 요청명을 가져옴 (* 아래에 추가 설명 있음)

 

1. if (action == null || action.equals("/listMembers.do")) 최초요청이거나 action값이 /listMembers.do 인 경우

  // )

  memberDAO.listMembers() 메서드를 이용해 전체 회원목록 리스트를 가져와 request에 "membersList"로 바인딩함

  nextPage = "/test02/listMembers.jsp"; // 로 포워딩함

  => listMembers.jsp 로 회원 목록 출력함

 

2. else if (action.equals("/addMember.do"))  action 값이 /addMember.do 인 경우

  request.getParameter("id") 등으로 id, pwd, name, email 가져와 String 변수에 저장 후 VO 객체 생성 후

  memberDAO.addMember(memberVO) 메서드 이용해 테이블에 넘어온 회원 정보 추가함

  nextPage = "/member/listMembers.do"; // 로 포워딩함

  => 회원 정보 추가 후 다시 회원 목록 출력함

  (지금 컨트롤러로 다시 요청돼서 위의 if문 통해 /test02/listMembers.jsp로 포워딩)

 

3. else if (action.equals("/memberForm.do"))  action 값이 /memberForm.do 인 경우

  nextPage = "/test02/memberFom.jsp"; // 로 포워딩함

  => 회원가입 창으로 이동함

 

... else // 그 외 다른 action 값

memberDAO.listMembers() 메서드로 회원 목록 리스트 얻어와 request에 바인딩,

nextPage = "/test02/listMembers.jsp"; 로 포워딩함

=> 다른 action 값들에 대해서는 회원 목록을 출력하도록 함

 


*** getRequestURI() 와 getPathInfo() 의 차이점

getRequestURI()

: 완전한 요청 URI를 반환함. 배포폴더 및 서블릿 매핑 문자열이 포함되며 모든 추가 경로 정보를 반환함

getPathInfo()

: 서블릿에 전달된 경로만 반환함. 전달된 추가 경로 정보가 없으면 null을 반환함

 

http://localhost:8080/deploy-folder/setvlet-mapping

이 요청은 deploy-folder 내부에 배포된 웹 애플리케이션의 servlet-mapping 서블릿에 도달함,

 

curl 명령으로 가져온 getRequestURI 요청에 대한 서블릿의 출력 :

curl http://localhost:8080/deploy-folder/servlet-mapping/request-path?function=getRequestURI

/deploy-folder/servlet-mapping/request-path

 

마찬가지로 getPathInfo()에 대한 서블릿의 출력

curl http://localhost:8080/deploy-folder/servlet-mapping/request-path?function=getPathInfo

/request-path

 

출처 https://recordsoflife.tistory.com/1399


[ listMember.jsp ] - 수정

복붙한 listMember.jsp에 회원가입 창으로 이동하는 a 태그를 추가함

 

<a href="${contextPath}/member/memberForm.do">회원가입하기</a>

* ${contextPath} 는 위에서 <c:set var="contextPath" value="${pageContext.request.contextPath}" /> 작성한 것 이용한거

 

 

[ MemberForm.jsp]

회원가입창. 회원정보를 입력하고 action 속성에서 /member/addMember.do로 요청하도록 작성함

 

form 태그의 action 속성 = ${contextPath}/member/addMember.do"

table 태그를 이용해 아이디, 비밀번호, 이름, 이메일 입력칸과

submit 타입 input('가입하기' 버튼), reset 타입 input('다시 입력' 버튼) 을 위치시킴

 

 

회원 정보 수정 및 삭제 기능 구현

685p

sec02.ex02 패키지 만들고 컨트롤러와 DAO, VO 복붙함, 

 

[ MemberController 클래스 ] - 수정

action 값이

4. /modMemberForm.do : 회원목록 조회 페이지에서 a태그로 get 방식 통해서 수정할 회원의 id와 함께 넘어옴, id로 테이블에서 검색해서 해당 회원 정보 VO 객체로 return함(수정 위한 작업임) => nextPage로 회원정보 수정 페이지 지정함

5. /modMember.do : 회원정보 수정 페이지에서 수정할 데이터 입력 후 '수정하기' 버튼 눌러서 요청됨, 

데이터는 VO 객체로 받아오고, 테이블에서 입력한대로 update 됨 => nextPage로 해당 컨트롤러의 목록조회 지정함

6. /delMember.do : 회원목록 조회 페이지에서 a태그로 get 방식 통해서 삭제할 회원 id와 함께 넘어옴,

id로 테이블에서 검색해서 해당 회원 정보 삭제함

 

4. else if (action.equals("/modMemberForm.do"))// listMembers.jsp에서 <a>태그 통해 GET방식으로 id와 함께 넘어옴

  String id = request.getParameter("id"); // id도 jsp에서 파라미터로 함께 넘어옴

  MemberVO memInfo = memberDAO.findMember(id);

  request.setAttribute("memInfo", memInfo)

  * Attribute는 server단에서 보낸 데이터가 들어가고 Parameter에는 client 단에서 보낸 데이터가 들어감

  // 회원정보 수정창을 요청하며 넘어온 id를 이용해서 수정 전 해당 회원 정보를 VO 객체로 가져옴, request에 바인딩

  nextPage = "/test03/modMemberForm.jsp";

  => 수정할 회원정보 객체를 가지고 회원정보 수정 페이지로 포워딩

 

5. else if (action.equals("/modMember.do")) // 회원정보 수정 페이지에서 수정할 데이터 입력 후 '수정하기' 버튼 누름

  getParameter() 로 id, pwd, name, email 받아와서 변수에 저장 후 VO 객체 생성

  memberDAO.modMember(memberVO); // 테이블에서 수정한 회원에 대한 정보를 update 함

  request.setAttribute("msg", "modified");  // 회원목록창(listMembers.jsp)에 수정완료 메세지 전달

  nextPage = "/member/listMembers.do"

  => 수정된 회원 정보가 포함된 회원 전체 목록 조회할 수 있도록 함

 

6. else if (action.equals("/delMember.do")) // listMembers.jsp에서 <a>태그 통해 GET 방식으로 삭제할 id와함께 넘어옴

   String id = request.getParameter("id"); // jsp에서 넘어온 삭제할 id 가져옴

  memberDAO.delMember(id);

  request.setAttribute("msg", "deleted"); // 회원목록창(listMembers.jsp)에 삭제 완료 메세지 전달

  nextPage = /member/listMembers.do";

  => 삭제된 회원 정보가 포함된 회원 전체 목록 조회할 수 있도록 함

...

RequestDispatcher dispatch = request.getRequestDispatcher(nextPage);

dispatch.forward(request, response);

// nextPage에 지정한 요청명으로 다시 서블릿에 요청함

 

  

[ MemberDAO 클래스 ] - 수정

회원 id를 이용해 회원 정보를 조회하고(findMember(String _id)),

수정 회원 정보를 갱신하고(modMember(MemberVO memberVO)),

회원 id로 회원 정보를 삭제하는 메서드(delMember(String _id))를 추가함

 

public MemberVO findMember(String _id)

{

// try-catch문

conn = dataFactory.getConnection();

String query = "select * from t_member where id=?"; // 전달된 id로 회원정보 조회함

pstmt = conn.preparedStatement(query);

pstmt.set(1, _id);

ResultSet rs = pstmt.executeQuery();

rs.next();

String id = rs.getString("id"); 등으로 id, pwd, name, email, joinDate 가져옴,

memInfo = new MemberVO(id, pwd,name, email, joinDate);

pstmt.close(); conn.close();

 

return memInfo

}

: 회원 id를 받아서 해당 회원의 정보를 테이블에서 찾아 VO 객체로 return해주는 메서드

 

public void modMember(MemberVO memberVO)

{

인자로 받은 VO객체에서 getter로 id, pwd, name, email을 얻어서 String 변수에 저장함

//try-catch문

conn = dataFactory.getConnection();

String query = "update t_member set pwd=?, name=?, email=? where id=?";

// where절로 id 조건 넣어줘서 해당 id 가진 회원의 pwd, name, email을 getter에서 얻어온대로 테이블에서 update함

pstmt = conn.preparedStatement(query);

pstmt.setString(1, pwd);

pstmt.setString(2, name);

pstmt.setString(3, email);

pstmt.setString(4, id); // 물음표 다 채움

pstmt.executeUpdate(); // update문이니까 executeQuery() 아니고 executeUpdate()임

pstmt.close(); conn.close();

}

: 인자로 id, pwd, name, email이 설정된 VO 객체를 받아서 VO객체에 저장된 대로 테이블에서 회원 정보 update함

 

public void delMember(String id)

{

conn = dataFactory.getConnection();

String query = "delete from t_member where id=?";

pstmt = conn.preparedStatement(query);

pstmt.setString(1, id);

pstmt.executeUpdate();

}

: 인자로 받은 id를 가진 회원 정보를 테이블에서 삭제함

 

 

[ listMembers.jsp ] - 수정

회원 추가, 수정, 삭제 작업 후 다시 회원 목록 창을 요청할 경우

컨트롤러에서 넘어온 msg 값에 따라 작업결과를 alert창에 출력함

회원 목록 출력 시 '수정', '삭제' 칸도 추가함

 

...

<head>

<c:choose>

<c:when test='${ msg == "addMember" }' >

  <script>

    window.onload = function() {

      alert("회원을 등록했습니다. ")

    }

  </script>

</c:when>

<c:when test='${ msg == "modified" }' >

  <script>

    window.onload = function() {

      alert("회원정보를 수정했습니다. ")

    }

  </script>

</c:when>

<c:when test='${ msg == "deleted" }' >

  <script>

    window.onload = function() {

      alert("회원정보를 삭제했습니다. ")

    }

  </script>

</c:when>

...

<body>

...

request에 바인딩된 membersList가 null이 아닌 경우(<c:choose> 에서 <c:when>으로 조건)

<c:forEach> 반복문으로 membersList의 요소를 출력함

 

<c:when test="${membersList != null }">

<c:forEach var="mem" item="${membersList}" > // membersList에서 요소 하나씩 꺼냄, 꺼낸 요소는 mem이라 함

<td>${mem.id }</td>

... 꺼낸 요소는 VO객체이고, 객체의 id, pwd, name, email, joinDate 를 표현언어를 이용해 출력함

<td><a href="${contextPath}/member/modMemberForm.do?id=${mem.id}">수정</a></td>

<td><a href="${contextPath}/member/delMember.do?id=${mem.id}">삭제</a></td>

// 회원 목록에 수정, 삭제 링크 칸도 추가함, id도 함께 전달함

...

 

 

[ modMemberForm.jsp ]

회원정보 수정 페이지.

바꾸고 싶은 회원 정보를 입력하고 '수정하기'를 클릭하면 action 속성에 설정한 요청명 /member/modMember.do 와 id를 전달해서 수정울 요청하도록 구현함.

 

...

<form method="post" action="${contextPath}/member/modMember.do?id=${memInfo.id}">

* 컨트롤러에서 modMemberForm.jsp로 포워딩될 때(action="/modMemberForm.do" 처리할때)

findMember() 메서드로 수정할 회원 정보를 VO 객체인 memInfo 반환받아서 request.setAttribute로 바인딩했었음

 

table 태그를 이용해 세로로 아이디, 비밀번호, 이름, 이메일, 가입일을 표시하는데,

<tr>

  <td width="200"><p align="right">아이디</p></td>

  <td width="400"><input type="text" name="id" value="${memInfo.id}" disabled></td>

  // input 텍스트 박스에 기본값이 표시되도록 설정(value 속성), 아이디는 수정불가(diasbled 속성)

</tr>

<tr>

  <td width="200"><p align="right">비밀번호</p></td>

  <td width="400"><input type="password" name="pwd" value="${memInfo.pwd}"></td>

</tr>

<tr>

  <td width="200"><p align="right">이름</p></td>

  <td width="400"><input type="text" name="name" value="${memInfo.name}"</td>

</tr>

<tr>

  <td width="200"><p align="right">이메일</p></td>

  <td width="400"><input type="text" name="email" value="${memInfo.email}"></td>

</tr>

<tr>

  <td width="200"><p align="right">가입일</p></td>

  <td width="400"><input type="text" name="joinDate" value="${memInfo.joinDate} disabled"></td>

</tr>

<tr align="center">

  <td colspan="2" width="400">

    <input type="submit" value="수정하기">

    <input type="reset" value="다시입력">

  </td>

</tr>

 

 

=>

1. 최초요청으로 나타난 회원목록창에서 id가 'cha2' 인 회원의 회원정보 수정을 위해 '수정' 을 클릭

2. http://localhost:8090/pro17/member/modMemberForm.do 로 요청됨

3. 회원정보 수정 페이지가 나타남, cha2 회원의 회원정보 기본값이 표시되고, 비밀번호, 이름, 이메일을 수정할 수 있음.

수정 후 '수정하기' 를 클릭

4. http://localhost:8090/pro17/member/modMember.do 로 요청됨

5. '수정 완료' 메세지가 alert창으로 나타나고, 수정된 회원 정보가 적용되어 회원목록이 표시됨

 

6. cha2 회원 정보 삭제를 위해 목록에서 '삭제' 버튼을 클릭

7. http://localhost:8090/pro17/member/delMember.do 가 요청되고, '삭제 완료' 메시지가 alert창으로 나타나며 회원 목록에서 해당 회원 정보가 사라진 것을 확인할 수 있음