1) Maven Repository - Global Repository에 다운받아둔게 있다면 pom.xml dependency에서 검색해서 가져올 수 있음.
(*홈페이지에서 dependency 코드가져와서 pom.xml에 붙여넣어도 됨)
라이브러리를 설정하고 저장(ctrl+s)하면 알아서 관련 라이브러리가 같이 들어온걸 볼 수 있음
2) layout.jsp파일에 설정하기
uri 설정해주기
#️⃣ URI와 URL이란?
👉 URI(Uniform Resource Identifier)와 URL(Uniform Resource Locator)은 웹에서 리소스를 식별하고 참조하기 위한 용어 👉 하지만 URI는 리소스를 식별하는 통합된 개념이고, URL은 리소스의 위치를 지정하는 구체적인 형태의 URI이다. (+URN은 리소스의 이름을 나타내는 URI 중 하나)
⏩ URI (Uniform Resource Identifier):
URI는 인터넷에서 리소스(문서, 이미지, 동영상 등)를 식별하는 통합된 방식을 나타내는 개념. URI는 URL과 URN(Uniform Resource Name)의 상위 개념. URI는 해당 리소스를 유일하게 식별하며, 리소스의 위치나 이름을 나타낼 수 있다.
⏩ URL (Uniform Resource Locator):
URL은 URI의 하위 개념 중 하나로, 리소스의 위치를 나타낸다. URL은 특정 리소스의 주소를 나타내며, 프로토콜(예: HTTP, FTP), 도메인 이름, 포트 번호, 경로 등의 정보로 리소스에 접근할 수 있는 경로를 지정한다. 예를 들어, "https://www.example.com/images/pic.jpg"는 URL로써 이미지 파일 "pic.jpg"가 위치한 주소를 나타낸다.
⏩URN (Uniform Resource Name):
URN은 리소스의 이름을 나타내는 URI의 한 유형. URN은 해당 리소스의 위치가 아니라 이름을 기반으로 식별된다. 예를 들어, "urn:isbn:0451450523"는 ISBN 번호를 가리키는 URN이다!
3) layout.jsp 각 속성에 위치 꽂아넣기
#️⃣ tiles.xml에 설정한 value를 getAsString으로 가져오기
1) tiles.xml 에서 value를 설정
2) layout.jsp에 가져오기 (title:getAsString)
👉 설정해둔 title을 기존의 title태그 자리에 넣어준다. 이때 기존에 있던 title태그들은 모두 지워준다!
✅ Tiles ViewResolver 설정하기
👉 요청을 받고 찾아서 반환해주려면 Resolver가 tiles 지시서를 찾도록 설정해줘야 한다
#️⃣ Resolver란?
리졸버(Resolver)는 컴퓨터 네트워크에서 도메인 이름을 IP 주소로 변환하거나, 반대로 IP 주소를 도메인 이름으로 변환하는 작업을 수행하는 시스템 또는 소프트웨어. 인터넷에서 정보를 찾거나 네트워크 연결을 설정할 때 중요한 역할을 하며, 웹 브라우징, 이메일 송수신, 파일 전송 등의 다양한 네트워크 활동에서 사용된다.
두 가지 주요 유형의 리졸버가 있다:
✔ DNS 리졸버 (Domain Name System Resolver):
DNS 리졸버는 도메인 이름을 IP 주소로 변환하거나, 반대로 IP 주소를 도메인 이름으로 변환하는 역할을 수행. 컴퓨터나 장치가 도메인 이름을 사용하여 특정 웹 사이트에 접속하려고 할 때, DNS 리졸버가 해당 도메인 이름을 해당 웹 사이트의 IP 주소로 변환하여 네트워크 통신을 가능하게 한다.
✔리버스 리졸버 (Reverse Resolver):
리버스 리졸버는 IP 주소를 도메인 이름으로 변환하는 역할을 한다. 주로 보안 로그 등에서 IP 주소가 기록되는 경우, 이 IP 주소를 실제 도메인 이름으로 변환하여 어떤 도메인에서 해당 IP 주소로 접근하려고 했는지 파악하는 데 사용된다.
#️⃣ Dispatcher-Servlet과 Resolver의 관계?
Dispatcher Servlet과 Resolver는 웹 애플리케이션 개발에서 사용되는 요소로, 서로 다른 역할을 수행한다.
✔ Dispatcher Servlet: Dispatcher Servlet은 Spring 프레임워크에서 웹 요청을 처리하고 적절한 핸들러(Controller)에게 전달하는 역할. 즉 클라이언트로부터의 HTTP 요청을 받아서 이를 처리할 적절한 컨트롤러나 핸들러로 라우팅해주는 중심 역할이다. 웹 애플리케이션의 컨트롤러와 뷰 리졸버를 관리하며, MVC 패턴을 따르는 웹 애플리케이션에서 사용된다.
✔ Resolver: Resolver는 Spring 프레임워크에서 뷰(View)를 결정하고 해당 뷰를 반환하는 역할. 뷰 리졸버는 뷰 이름을 실제 뷰 객체로 변환하여 Dispatcher Servlet이 해당 뷰를 클라이언트에게 반환할 수 있게 한다.
✔ Dispatcher Servlet과 Resolver의 관계: Dispatcher Servlet은 클라이언트의 요청을 받아서 적절한 컨트롤러로 전달하고, 컨트롤러가 처리한 결과를 뷰 리졸버를 통해 실제 뷰로 변환하여 클라이언트에게 반환한다. 이 때, 뷰 리졸버가 뷰의 이름을 실제 뷰 객체로 변환하는 역할을 수행한다. 이를 통해 Dispatcher Servlet은 클라이언트와 컨트롤러 간의 연결을 담당하고, Resolver는 뷰를 결정하고 처리한 결과를 클라이언트에게 보여주는 역할을 수행한다. 따라서 Dispatcher Servlet과 Resolver는 웹 애플리케이션의 요청-처리-응답 흐름에서 함께 동작하여 완전한 웹 페이지 생성과 제공을 담당한다.
👉 Dispatcher-servlet.xml 설정
jsp를 처리할 라이브러리가 없어서 찾질못하는 중pom.xml에 가서 jstl라이브러리를 추가해준다
#️⃣ JSTL이란? JSTL(JavaServer Pages Standard Tag Library)은 Java 웹 애플리케이션에서 웹 페이지를 구성하고 동적으로 데이터를 처리하기 위한 표준 태그 라이브러리. JSTL은 JSP(JavaServer Pages)에서 사용되며, HTML 코드 안에 자바 코드를 혼합하지 않고 웹 페이지를 더 깔끔하고 유지보수 가능한 방식으로 작성하는 데 도움을 준다.
* 공지사항 & detail controller 추가 및 jsp 수정 파트는 모두 동일해서 공지사항으로만 정리함*
[1] html 파일 > jsp 파일로 변경
세팅만 되어있다면야 jsp화 시키는건 쉽다. 쉬운데... 컨트롤러랑 이어주는게 문제.
[2] 공지사항 컨트롤러 생성
: 현재 뷰는 WEB-INF - view - notice 아래에 각각 list.jsp, detail.jsp로 생성되어 있다. ▶ 해당 뷰 경로로 컨트롤러가 반환할 수 있게 잡아주기.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class ListController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView("notice/list"); // 뷰가 있는 정보 전달
//mv.setViewName("/WEB-INF/view/notice/list.jsp");
return mv;
}
}
👉 컨트롤러를 통해 값을 반환받아 뷰로 내보낼 경로를 지정해주기.
[3] 컨트롤러 만든 후엔 dispatcher servlet에 url - mapping✨
❣❣❣❣❣ 이 부분 까먹어서 detailController 만들고 jsp 수정해놓고도 연결이 안됨! ❣❣❣❣❣
dispatcher-servlet.xml 에 빈 추가. id는 컨트롤러에 설정해준 매핑과 일치시키면 된다. 여긴 디테일까지 추가된 화면을 캡쳐해왔네.
[4] 서버 구동 - JSP 반환되는지 확인하기
👉 서버를 돌려서 공지사항으로 가기 위한 메뉴에 커서를 올렸을때, 왼쪽 하단에 경로가 list.html로 뜬다면, 이건 컨트롤러를 통해 jsp로 반환되는게 아니라 기존에 정적화면으로 반환되던 html과 연결되어 있다는 뜻.
list.html > list
👉 해당 페이지인 index.jsp 내부에 가서 해당 버튼과연결된 하이퍼링크를 확인하고, jsp로 연결해준다. 이동된 화면에서도 주소창에 html이 아닌지 더블체크 해주기.
✅ 즉, dispatcher-servlet.xml에서는 url을 보고 어떤 객체를 끌어올건지 세팅해놓는 것.
✅ 각 jsp에서는 버튼을 눌러 이동시에 원하는 url로 들어가 있는지 확인하는 것.
<순서>
url ▶ dispatcher-servlet로 해당 클래스(컨트롤러)로 이동 ▶ Controller에서 정리된 값과 뷰 전달 ▶ 모델 받고 jsp 로 view 송출 (한바퀴 완) ▶ 클라에서 원하는 버튼 누르기 ▶ url 전송 ▶ dispatcher-servlet ▶ Controller ▶ 값 받아서 view보내기 (두바퀴 완)
[5] 뷰 집중화 필요성
: index.jsp의 헤더에 있는 '고객센터' 메뉴의 list.html 을 jsp로 변경해놓았는데, list.jsp, detail.jsp 에 있는 '고객센터'메뉴(list.html)도 모두 일일히 jsp로 수정해줘야하는 상황이 발생한다.
: 톰캣은 받은 데이터를 모두 전송. 톰캣의 web.xml에 있던 url 값들은 Dispatcher서블릿에 설정된다.
: Dispatcher는 여러 컨트롤러중, 필요한 컨트롤러를 사용하여 model을 반환받고, dispatcher은 model, view값을 jsp에 넘겨서 결과페이지를 반환하게 한다.
: Dispatcher 역할의 집중화 = mvc모델의 발전
POJO: plain old object java 순수한 자바파일
여기서 Maven은 Java Resources ▶ Libraries ▶ Maven Dependencies 에 있는 라이브러리를 참조하고(+pom.xml), Tomcat은 로컬에서 src ▶ main ▶ webapp ▶ WEB-INF ▶ lib 에 있는 라이브러리를 참조한다.(+web.xml)
* sts설치시 압축폴더오류: .jar > .zip 으로 변경했고, 윈도우 내장프로그램으론 .contents 압축을 풀 수 없어서 반디집 깔아서 사용함
: The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path ▶ maven repository에서 maven 버전에 맞게 dependency받아다 pom.xml에 저장하기
: mvc 활용을 위해 spring web mvc dependency도 추가하기
2-5) 그외
: window-web browser-default system browser 설정 (내장브라우저 쓰기싫으면!)
: jsp에서 톰캣 최초 실행시 해당 버전 & 홈디렉토리 연결 (bin폴더가 있는 화면까지 가야 홈디렉토리)
[3] Spring Dispatcher를 Front Controller로 설정하기
: 웹프로젝트에 spring을 얹는 과정
: spring-webmvc.jar 안에 DispatcherServlet.class가 있으므로 이를 다운받아야 한다 ▶ maven repository에서 spring webmvc dependency 추가
libraries - maven dependencies에서 확인 가능
3-1) Dispatcher-Servlet.xml 생성하기
: 컨트롤러는 순수 저바 코드로 남겨두고, 서블릿에 관한 url 은 web.xml이 모든 URL을 받도록 설정한다. (받아서 dispatcher Servlet.xml에게 넘김)
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/dispatcher-servlet.xml];
👉 web.xml에서 넘겼는데 받아서 정리할 -servlet.xml 이 없으므로 500에러 반환. 이때 내가지정한 dispatcher이름(지정어)+servlet.xml(예약어)이 WEB-INF폴더 안에 있어주면된다.
//web.xml
<!-- dispatcherServlet 설정 -->
<servlet>
<servlet-name>dispatcher</servlet-name> //이름을 dispatcher로 지어서 오류에도 dispatcher로 나옴
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<!-- 모든 url mapping -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
//url 패턴을 /*로 설정하면, 다 돌고 mvc값을 가져온 리턴값이 다시 dispatcher로 돌아가서 일치하는 애를 찾게 되면서, 해당 jsp가 없으면 무한루프로 갇히게 된다.
//그냥 '/'로 설정하면 리턴값이 dispatcher에 없을때 그대로 출력한다
dispatcher library가 사용하는 설정파일 dispatcher-servlet.xml
* dispatcher-servlet 설정파일 - IoC container - Core Technologies
: servlet & jsp 때는 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {equest.getRequestDispatcher("/WEB-INF/view/index.jsp").forward(request, response);} 으로 값을 받고 다른페이지로 연결하는데만 중심을 두었다면, (url은 @WebServlet("/index") 로 컨트롤러에 붙어있었다)
: 디스패쳐를 통한 컨트롤러는 dispatcher Servlet이 url을 모두 가지고 있으므로 (디스패쳐가 forward하는 담당), public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {...}는 model and view 객체를 반환한다
디스패쳐, 컨트롤러 모두 절대경로로 되어있어야 알맞게 나온다경로에서 프로젝트명 숨기기컨텍스트 명을 바꿨을 경우는 서버에 와서 기존 prj를 지울것.
[5] View Resolver
: 반복되는 view의 일부분을 알아서 붙여준다
: 컨트롤러의 ModelAndView에 jsp파일명만 쓸 수 있다! 👉 반복되는 '/WEB-INF/view/ .jsp'는 view resolver의 역할.
ModelAndView mv = new ModelAndView("index");
* view resolver가 없으면 무한루프로 찾다가 오류메시지가 나온다
메시지Circular view path [index]: would dispatch back to the current handler URL [/index] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
👉 이 프로그램을 구현/테스트 하기 위한 주업무 외의 로직이 필요. 이는 개발자, 운영자가 필요를 위해 만드는 것. (사용자는 모름)
✔ Concern
핵심기능 : Primary (Core) Concern 는 객체로 만들어짐. 실제 업무는 메서드로 만들어짐
부가기능 (관점이 다른 로직) : Cross-cutting Concern (예: 로그처리 (성능테스트 /요구권한 등), 보안처리, 트랜잭션 처리
✔ AOP
주 업무 틀만 만들어놓고 (소스코드 없음) Proxy를 만들어서 부가기능을 사용하게 한다
AOP의 구현 방식
👉 Proxy를 꽂고 빼는 부분은 Spring DI로 해결 가능 (코드 변경과 수정 문제)
[2] 순수 자바로 AOP구현해보기
: 프로그램은 proxy - 핵심 업무 - proxy의 순서를 거쳐 출력된다
: 필요에 따라 핵심기능만 출력할수도 있고 (exam.total()) 곁다리가 포함된 값을 출력할 수도 있다 (proxy.total())
: 해당 객체 내의 다른 메서드도 호출 가능하다 (proxy.avg())
//Program.java proxy가 있는 부가기능
public class Program {
public static void main(String[] args) {
Exam exam = new NewlecExam(1, 1, 1, 1);
// proxy만들기
// loader-실제업무호출, interface's' - 클래스를 배열형태로 전달, h-곁다리 업무를 꽂는부분(핸들러)
Exam proxy = (Exam) Proxy.newProxyInstance(NewlecExam.class.getClassLoader(), new Class[] { Exam.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
// 실제업무 호출 코드 작성
Object result = method.invoke(exam, args); // args가 실제 업무 반환
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 걸렸습니다";
System.out.println(message);
return result;
}
});
System.out.printf("total is %d%n", proxy.total()); // 곁다리 + 핵심기능. 걸린시간 + total is 4
//System.out.printf("total is %d%n", exam.total()); // 핵심기능 출력 total is 4
System.out.printf("agv is %3.2f%n", proxy.avg()); // 필요시 avg에도 곁다리를 끼워넣을수 있음
}
}
//NewlecExam.java
public class NewlecExam implements Exam {
private int kor;
private int eng;
private int math;
private int com;
//기본생성자 생략
public NewlecExam(int kor, int eng, int math, int com) {
this.kor = kor;
this.eng = eng;
this.math = math;
this.com = com;
}
@Override
public int total() { // 메인 메서드 1
//long start = System.currentTimeMillis(); //곁다리
int result= kor + eng + math + com;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//long end = System.currentTimeMillis(); //곁다리
//String message = (end-start)+"ms 시간이 걸렸습니다";
//System.out.println(message);
return result;
}
@Override
public float avg() { // 메인 메서드 2
//곁다리 업무 들어가는 공간
float result = total() / 4.0f;
//곁다리 업무 들어가는 공간
return result;
}
[3] 스프링 AOP로 AroundAdvice구현하기
: 스프링에서는 부가기능업무를 4가지로 구분하여 제공
✔ BeforeAdvice - 핵심기능 앞에만 부가기능 붙이기
✔AfterReturnningAdvice- 핵심기능 결과값 반환 뒤에 부가기능붙이기
✔AfterThrowingAdvice - 예외처리
✔AroundAdvice - 앞뒤 모두 부가기능붙이기
3-1) xml을 통해 만들기
✔ xml에 생성해야 할 것
① 객체 생성 코드
② proxy객체 생성 코드
③ 부가 기능 코드
//setting.xml
<!-- Exam exam = new NewlecExam(1, 1, 1, 1); Exam객체를 target으로 명명함 -->
<bean id="target" class="spring.aop.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1" />
<bean id="logAroundAdvice" class="spring.aop.advice.LogAroundAdvice"/>
<!--Exam proxy = (Exam) Proxy.newProxyInstance -->
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target" /> //setExam과 마찬가지
<property name="interceptorNames"> <!-- 타겟에 해당되는 것, 핸들러 설정 -->
<list>
<value>logAroundAdvice</value> //핵심 기능 값을 불러오는 invoke를 클래스 생성으로 구현예정
</list>
</property>
</bean>
</beans>
✔ AroundAdvice만들기
//LogAroundAdvice.java
package spring.aop.advice; //xml에 넣은 패키지명과 동일
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class LogAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed(); //타겟을 따로 넘겨받는일 없이 깔끔하게 처리. method.invoke(exam, args); 이부분임.
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 걸렸습니다";
System.out.println(message);
return result;
}
}
-----------------------
//Program.java는 기능을 모두 넘겨주고 출력 기능만 남아있다.
public class Program {
public static void main(String[] args) {
ApplicationContext context =
// new AnnotationConfigApplicationContext(NewlecDIConfig.class);
new ClassPathXmlApplicationContext("spring/aop/setting.xml");
Exam proxy = (Exam) context.getBean("proxy");
System.out.printf("total is %d%n", proxy.total()); // 곁다리 + 핵심기능. 걸린시간 + total is 4
System.out.printf("agv is %3.2f%n", proxy.avg());
👉 여기서 Program & xml의 proxy를 exam으로 모두 교체하면 사용자 입장에서는 proxy값이 핵심기능인지 부가기능인지 알 수 없다.
👉 만약 xml에서 proxy를 모두 지우고 핵심기능인 target 을 exam으로 변경하면 핵심기능만 출력된다.
[4] BeforeAdvice 구현하기
: xml 내에 설정한 핸들러는 list로 값을 받으므로 BeforeAdvice, AroundAdvice 두개 다 설정해서 사용할 수 있다.
: throwAdvice가 구현할 내용이 고정될 수 없으므로 디폴트 메서드만 구현되어있다.
//Program.java
public int total() {
int result= kor + eng + math + com;
if(kor>100) // 예외사항이 발생할경우
throw new IllegalArgumentException("유효하지 않은 국어점수");
//sleep관련 try/catch문 생략
return result;
}
------------------
//LogAfterThrowingAdvice.java
package spring.aop.advice;
import org.springframework.aop.ThrowsAdvice;
public class LogAfterThrowingAdvice implements ThrowsAdvice {
// 어떤 예외를 처리할건지는 본인이 설정. 여기선 IllegalArgumentException을 설정
public void afterThrowing(IllegalArgumentException e) throws Throwable{
System.out.println("예외가 발생하였습니다.: " + e.getMessage());
}
}
[6] Point Cut (Weaving, Join Point)
✔ Weaving : Cross-cutting Concern과 Core Concern 을 엮는 과정
✔ JoinPoint : Core Concern 에서 엮일 메인 함수 .proxy는 모든 메서드를 조인포인트로 생각한다. (total(), avg())