본문 바로가기
카테고리 없음

spring 평가 대비

by why제곱 2023. 5. 1.

1. Spring DI

  • Spring Framework의 특징
    • POJO(Plain Old Java Object) 방식의 프레임워크
      • POJO란?
        • 오래된 방식의 간단한 자바 오브젝트
        • JavaEE 등의 중량 프레임워크들을 사용하며 해당 프레임워크에 종속된 무거운 객체를 만들게 된 것에 반발해 사용하게 된 용어
        • 특정 기술에 종속되어 동작하는 것이 아닌 순수한 자바 객체를 의미함
        • 왜 POJO를 지향해야 하는가?
          • 가독성 떨어지고 유지보수의 어려움
          • 특정 기술의 클래스를 상속받거나, 직접 의존하게 되어 확장성이 매우 떨어지는 단점
          ⇒ Java의 장점인 객체지향 설계의 장점을 잃어버리게 된 것
      • 특정 기술을 사용하고 싶다면? (스프링이 POJO를 유지하면서 Hibernate를 사용할 수 있는 이유) ⇒ PSA
    • 의존성 주입(Dependency Injection)을 통한 객체 관계 구성
    • 관점지향 프로그래밍(AOP, Aspect Oriented Programming)
    • 제어 역전(IoC, Inversion of Control)
    • 높은 확장성과 다양한 라이브러리
  • ClassA 객체가 어떤 일을 처리하기 위해서 ClassB 객체의 도움을 받아야만 일을 처리할 수 있다면 ‘ClassA는 ClassB에 의존한다’라고 표현
  • 위 예제에서는 Programmer가 Desktop Class에 의존성을 가지고 있음.
  • Programmer가 컴퓨터를 laptop으로 바꾸고 싶다면 코드 수정이 필요함
    • 현재 객체 생성의존성과 타입 의존성 이렇게 두 가지를 가지고 있음
    • 이걸 의존 안하고 싶다면? new 를 하지 않고 내가 다른 곳에서 만든 객체를 전달해주기만 하면 됨.

 

  • Container ?
    • 스프링에서 핵심적인 역할을 하는 객체를 Bean이라고 함
    • Container는 Bean의 인스턴스화 조립, 관리의 역할, 사용 소멸에 대한 처리를 담당
    • BeanFactory
      • 프레임워크 설정과 기본기능을 제공하는 컨테이너
      • 모든 유형의 객체를 관리할 수 있는 메커니즘 제공
    • ApplicationContext
      • BeanFactory 하위 인터페이스
      • 이벤트 처리, 국제화용 메시지 처리, AOP 통합 기능 제공
    • WebApplicationContext
      • 웹 환경에서 Spring을 사용하기 위한 기능이 추가됨
      • 대표적인 구현클래스로 XMLWebApplicationContext가 있음
  • 스프링 설정 정보
    • 애플리케이션 작성을 위해 생성할 Bean과 설정 정보, 의존성 등의 방법을 나타내는 정보
    • 설정정보를 작성하는 방식은 XML방식, Annotation방식, Java방식이 있음

 

    • 의존성 주입 생성자
      • constructor-arg 를 이용하여 의존성 주입
      • <ref>, <value>와 같이 하위 태그를 이용하여 설정 or 속성을 이용하여 설정
      • 위와 같이 설정하면, container가 build될 당시에 Programmer를 만들 당시에 desktop을 먼저 만들고 이를 Programmer에 주입시켜놓음
      • bean의 순서가 달라도 문제 없음. 의존성 주입에 순서는 중요하지 않음.
  • 의존성 주입(설정자)
    • setter를 이용하여 의존성 주입
    • <ref>, <value>와 같이 하위 태그를 이용하여 설정 또는 속성을 이용하여 설정
    • <property name = “setter의 이름” ref = “ ~ “ > </property>
  • 빈(Bean) 생성 및 설정(@Component)
    • 빈(Bean) 생성 - @Component : 객체 생성할 대상 클래스에 작성해주는 Annotation
    • 생성되는 bean의 이름은 클래스의 첫 글자를 소문자로 바꾼 것
    • 스프링은 @Component, @Service, @Controller, @Repository 의 Stereotype Annotation을 제공
    • 각 Repository, Service, Controller는 목적에 맞는 구체적인 사용을 위한 @Component의 확장. 목적에 맞게 구체화하여 사용하면 Spring에서 더 효율적으로 사용 가능
    • Annotation 방식으로 Bean을 등록하기 위해서는 의존성 설정을 위해 대상 패키지를 지정해야 함
<context:component-scan base-package = "com.ssafy.di"></context:component-scan>
  • 의존성 주입(@Autowired)
    • 의존성을 주입할 대상에 가서 Autowired annotation 작성
    • Spring 컨테이너 내에서 타입 매칭 시행(컨테이너에 해당되는 타입 bean이 있다면 매칭)
  • Autowired 사용 가능 위치
    • 생성자 : 생성자를 하나만 정의한다면 생략 가능
    • Setter : Qualifier를 이용하여 같은 타입이 여러 개일 경우, bean을 지정하여 식별 가능
    • field : Qualifier를 이용하여 같은 타입이 여러 개일 경우, bean을 지정하여 식별 가능
  • Qualifier : 느슨한 결합을 함으로써 같은 인터페이스 타입이 Bean에 등록되어 있을 때 그들 중 특정 하나만 지정하기 위해 사용.
    • Qualifier가 다 살아있을 땐(하나는 field, 하나는 setter) 둘 중 어떤 Qualifier 중 어떤게 살아남을까 ?
      • field보다는 Setter가 더 세다 !!

 

  • AOP(Aspect Oriented Programming)
    • OOP(객체지향)에서 모듈화의 핵심 단위는 클래스인 반면, AOP에서 모듈화 단위는 Aspect
    • OOP를 더 발전시키기 위한 개념. 하나의 소프트웨어가 거대한 OOP로 설계, 프로그래밍되어 있다면 이를 각 기능별로 모듈화해서 분리시키는 개념
    • AOP의 핵심 : 공통 모듈을 분리시켜 해당 소스코드가 외부의 다른 클래스에 존재하는 것
    • 핵심관심(각 서비스의 핵심 비즈니스 로직)과 횡단 관심(공통 모듈 ex. Security, Profiling, Logging, Transaction Management)
    • Aspect는 여러 타입과 객체에 거쳐 사용되는 기능(Cross Cutting, 트랜잭션 관리 등)의 모듈화
    • 횡단 관심사는 비즈니스 기능과는 별개의 영역이지만 필연적으로 대다수의 비즈니스 기능에 분포되어 있으며 이는 가독성을 떨어뜨리고 자칫 중복된 코드 생겨날 가능성. 비즈니스의 모듈성을 감소시키는 가장 큰 요인.
    • Spring Framework의 필수 요소는 아니지만 AOP프레임워크는 SpringIoC를 보완
    • 장점
      • 각 비즈니스 로직마다 복붙을 통해 생겨난 중복 코드 제거 가능
      • 각 비즈니스 로직을 구현하는 개발자는 자기 자신의 코드에만 집중할 수 있음
      • 코드 간결, 유지보수 쉬워짐
      • 재활용성 높아짐
  • 용어
    • Aspect : 여러 클래스에 공통적으로 구현되는 관심사의 모듈화
      • logging하는 기능, transaction하는 기능, 보완적인 기능 등
    • Join Point : 메서드의 실행이나 예외처리와 같은 프로그램 실행 중의 지점들(여러 위치). Spring에서는 메서드 실행을 의미함(공통 관심사들이 들어갈 수 있는 위치)
    • Pointcut : Join Point에 Aspect를 적용하기 위한 조건 서술. Aspect는 지정한 pointcut에 일치하는 모든 join point에서 실행됨(특정 위치)
    • Advice : 특정 조인포인트에서 Aspect에 의해 취해진 행동, Around, Before, After 등의 Advice 타입 존재
    • Target 객체 : 하나 이상의 Advice가 적용될 객체, Spring AOP는 Runtime Proxy를 사용하여 구현되므로 객체는 항상 Proxy객체가 됨.
    • AOP Proxy : AOP를 구현하기 위해 AOP 프레임워크에 의해 생성된 객체, Spring Framework에서 AOP 프록시는 JDK dynamic proxy 또는 CGLIB proxy이다.
    • Weaving : Aspect를 다른 객체와 연결하여 Advice 객체 생성. 런타임 또는 로딩 시 수행할 수 있지만 Spring AOP는 런타임에 위빙을 수행. 특정 JoinPoint에 Advice하여 핵심기능과 횡단기능이 교차하여 새롭개 생성된 객체를 프로세스에 적용하는 일련의 모든 과정을 말함.
  • Spring AOP Proxy
    • 실제 기능이 구현된 Target객체를 호출하면, target이 호출되는 것이 아니라 advice가 적용된 Proxy객체가 호출됨
    • Spring AOP는 기본값으로 표준 JDK dynamic proxy를 사용
    • 인터페이스를 구현한 클래스가 아닌 경우 CGLIB프록시를 사용
  • SpringAOP
    • @AspectJ : 일반 Java클래스를 Aspect로 선언하는 스타일. AspectJ에 의해 소개됨
    • Spring AOP에서는 pointcut 구문 분석, 매핑을 위해 AspectJ라이브러리를 사용함
    • 하지만 AOP runtime은 순수 Spring AOP이며 AspectJ에 대한 종속성은 없음

Intercepter

  • HandlerIntercepter(인터페이스)를 구현한 것. 또는 HandlerIntercepterAdapter(클래스)를 상속한 것
  • 요청을 처리하는 과정에서 요청을 가로채서 처리
  • 접근제어(Auth), 로그(log) 등 비즈니스 로직과 구분되는 반복적이고 부수적인 로직 처리
  • HandlerIntercepter의 주요 메서드
    • preHandle()
      • Controller(핸들러) 실행 이전에 호출
      • false를 반환하면 요청을 종료
      • 클라이언트가 서버에 요청을 날릴텐데 인터셉터가 이걸 가로채서 판단을 하고 true를 반환하면 계속 진행, false를 반환하면 Controller까지 가지 못 함.
    • postHandle()
      • Controller 실행 후 호출
      • 정상 실행 후 추가 기능 구현시 사용
      • Controller에서 예외 발생 시 해당 메서드 실행되지 않음
    • afterCompletion()
      • 뷰가 클라이언트에게 응답 전송한 뒤 실행
      • Conroller에서 예외 발생 시, 네번째 파라미터로 전달이 됨(기본은 null)
      • 따라서 Controller에서 발생한 예외 혹은 실행시간 같은 것들을 기록하는 등 후처리 시 주로 사용
    • 코드
package com.ssafy.mvc.interceptor;

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

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

//이거 인터셉터로 할랭
//1. 구현  implements HandlerInterceptor 
//2. 상속extends HandlerInterceptorAdapter 
//2번 했더니 왜 줄 생겨? 이거 옛날 방식이고 이제 좀 안썼으면 좋겠다는 의미.
//지금은 괜찮아. 써도 동작은 하는데 추후 동작 안할 수 도 있으니!
public class AInterceptor implements HandlerInterceptor {
	
	//아래 메서드를 빈으로 등록해야 쓸 수 있음.
	//Intercept는 웹과 관련된 설정인가 혹은 그 외의 설정인가?
	//웹과 관련된 설정임 => Servlet.context에 저장 됨.
	
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("A : preHandle");
		return true;
	}
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("A : postHnadle");
	}
	
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// TODO Auto-generated method stub
		
		System.out.println("A : afterCompletion");
	
	}
	
	
	
}

 

인터셉터 등록(xml에)

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans

	xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing 
		infrastructure -->

	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving 
		up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources 
		in the /WEB-INF/views directory -->
	<beans:bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

	<context:component-scan
		base-package="com.ssafy.mvc.controller" />
		
		<beans:bean class = "com.ssafy.mvc.interceptor.LoginInterceptor" id = "confirm"></beans:bean>
	<interceptors>
		 <interceptor>
			<mapping path="/*" />
			<beans:bean class = "com.ssafy.mvc.interceptor.AInterceptor"> </beans:bean>
		</interceptor>
		
		<interceptor>
			<mapping path="/*" />
			<beans:bean class = "com.ssafy.mvc.interceptor.BInterceptor"> </beans:bean>
		</interceptor>
		
		<interceptor>
			<mapping path="/*" />
			<beans:bean class = "com.ssafy.mvc.interceptor.CInterceptor"> </beans:bean>
		</interceptor>
	
	</interceptors>



</beans:beans>

 

package com.mvc.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;

public class LoginInterceptor implements HandlerInterceptor {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//여기에서 검사 진행
		//로그인 정보가 있는지, 로그인 했는지에 대한 정보는 주로 session에 저장
		
		//세션은 이렇게 가져올 것!
		HttpSession session = request.getSession();
		//로그인할 때 이렇게 id를 세션에 담아둘 거고! 그 id를 가져와봤을 때 null이면
	//로그인 상태가 아니라는 의미
		if(session.getAttribute("id") == null){
			response.sendRedirect("login");
			return false;
		}

		return true; //통과
	}
}

- 파일 입출력

코드 

package com.ssafy.mvc.controller;

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.ServletContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;



@Controller
public class MainController {
	@Autowired
	private ServletContext servletContext;
	//resource 경로를 가져오기 위해 사용
	@Autowired
	private ResourceLoader resLoader;

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home() {
		
		return "home";
	}
	
	
	@GetMapping("regist")
	public String registForm() {
		return "regist";
		
	}
	
	@PostMapping("upload")
	public String upload(MultipartFile upload_file, Model model) {
		//실제 저장될 위치
		String uploadPath = servletContext.getRealPath("/upload");
		//위의 값이 실제 파일 위치와 다른 이유?
		//run on server를 하면 프로젝트를 톰캣에 실어서 실행 
		//실제 실행되는 서버의 주소인 것.
		//파일 업로드를 했을 때는 프로젝트 폴더가 아니라 톰캣에 등록되는 것.
		
		
		//만약 등록하려고 하는 upload폴더가 없을 수도 있다.
		//그러면 그 폴더를 만들자.
		File folder = new File(uploadPath);
		if(!folder.exists()) folder.mkdir();
		
		//실제 파일 이름
		String fileName = upload_file.getOriginalFilename();
		File target = new File(uploadPath, fileName);
		
		//파일을 해당 폴더에 저장하자
		//저장방법은 크게 두가지
		//1. FileCopyUtiles 이용
		try {
			FileCopyUtils.copy(upload_file.getBytes(), target);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("upload : " + fileName);
		model.addAttribute("fileName", fileName);
		//2. File인스턴스 이용
		
		return "result";
	}
	
	

	@PostMapping("upload2")
	public String upload2(MultipartFile[] upload_files, Model model) throws IOException {
		
		//파일들의 이름을 저장할 리스트를 생성하자(임시)
		List<String> list = new ArrayList<String>();
			
		//지금은 단순히 upload라고 하는 폴더에 그대로 파일을 저장하고 있지만
		//폴더 구조를 잡고 저장하는 것이 더 있어보인다.(?)
		//파일 이름도 실제 저장할 때는 OriginName / 저장할 이름 두가지로 나누어 저장 하는 것도 하나의 방법
		//같은 이름을 사용하면 덮어버리기 때문
		
		
		//사전에 null이 아닌지 확인해주는게 좀 더 안전함
		if(upload_files != null) {
			Resource res = resLoader.getResource("resources/upload");
			if(!res.getFile().exists()) res.getFile().mkdir();
			
			for(MultipartFile mfile : upload_files) {
			//조금 더 커팅해보면
				if(mfile.getSize()>0) { //파일이 있으면
					mfile.transferTo(new File(res.getFile(), mfile.getOriginalFilename()));
					list.add(mfile.getOriginalFilename());
				}
			}
		}
		
		model.addAttribute("list", list);
		return "result";
		
	}
	
	@GetMapping("download")
	public String download(Model model, String fileName) {
		Map<String, Object> fileInfo = new HashMap<String, Object>();
		fileInfo.put("fileName", fileName);
//		System.out.println(fileName);
		model.addAttribute("downloadFile", fileInfo);
//		model.addAttribute("downloadFile", fileName);
		return "fileDownLoadView";
		
	}
	

	
	
}
	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />
	<resources  mapping="/upload/**" location="/upload/"></resources>
	

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.ssafy.mvc.controller" />
	
	<beans:bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" 
		id="multipartResolver">
		<beans:property name="defaultEncoding" value="UTF-8"/>
		<!-- 용량 작성 단위는 바이트 단위를 하겠다. -->
		<beans:property name="maxUploadSize" value="10485760"/> 
	</beans:bean>
	
	<beans:bean class="com.ssafy.mvc.view.FileDownLoadView" id="fileDownLoadView"/>
	<beans:bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
		<beans:property name="order" value="0"></beans:property>
	</beans:bean>
  • MyBatis 구성
    • 환경설정 파일
      • MyBatis 전반에 걸친 세팅
      • DB접속정보, 모델 클래스 정보, 매핑 정보
    • Mapper
      • 사용할 SQL문 정의
    • Mapped Statement
      • SqlSession 빌드 및 사용
      • SQL문 실행
    • Input/Output
      • SQL 실행 시 필요한 데이터
      • SQL 실행 결과
    • SqlSessionFactory
      • 모든 마이바티스 애플리케이션은 이 인스턴스 사용\
      • sql와 MyBatis 연
      • SqlSessionFactory는 SqlSession을 만듦
    • SqlSession
      • 데이터베이스에 대해 SQL 명령어를 실행하기 위한 메서드 포함
    • SqlSessionFactory 빌드하기(예시)
String resource = "config/mybatis-config.xml";
Reader reader = Resources.getResourcesAsReader(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader));
  • 별칭 정하기 (편의를 위한 것!!)
    • 패키지에 대한 별명 지정 가능. type에 풀 패키지 명. alias에는 별명 지정
<typeAliases>
		<typeAlias type="com.ssafy.board.model.dto.Board" alias ="Board" />
	</typeAliases>

 

 

https://hip-tuck-d59.notion.site/2023-04-25-REST-API-84459e60dbc7483791e544aab0467f87

 

2023-04-25 REST API

REST API

hip-tuck-d59.notion.site

https://hip-tuck-d59.notion.site/2023-04-26-Swagger-71e3e9c83ede4ad0aa170b41f317287c

 

2023-04-26 Swagger

@Controller 뷰 리졸버가 낚아채버리는데, 뷰 리졸버가 아니라 순수하게 데이터 형태로 반환하고 싶을 때 @Responsebody라는 걸 썼음.

hip-tuck-d59.notion.site

 

properties

typeAliases

environments

mappers