본문 바로가기
스프링

[스프링] 자료실 , bean 개념

by CodeMango 2023. 2. 28.

스프링의 빈이란?

"빈(bean)"은 Spring Framework에서 제공하는 객체를 말합니다.

스프링에서는 빈을 개발자가 정의하고 등록하여 필요한 곳에서 사용할 수 있습니다.

 

빈을 등록하면 Spring 컨테이너가 객체를 생성하고, 라이프사이클 관리 및 의존성 주입(Dependency Injection) 등의 작업을 수행합니다.

빈은 스프링의 핵심 기능 중 하나이며, 스프링의 IoC(Inversion of Control) 기능을 구현하는 데 있어서 중요한 역할을 합니다.

 

스프링은 개발자가 정의한 빈 객체를 생성하고, 관리하며, 필요한 곳에서 빈을 주입해주는 등의 작업을 수행함으로써, 객체지향 설계의 다양한 원칙(예: DIP, OCP, SRP)을 따르는 프로그램을 작성할 수 있도록 도와줍니다.

 

따라서, 빈은 스프링 프레임워크에서 객체 생성과 관리를 담당하는 중요한 역할을 수행합니다.

 

개발자가 정의한 빈 객체를 이용하여 애플리케이션을 개발하고, 스프링의 다양한 기능을 이용하여 빈을 관리하고 의존성을 주입하는 등의 작업을 수행합니다.

 

 

자료실 

게시판 + 파일 업 다운로드

 

파일업로드

client에서 데이터 넘겨주면 server에서 읽어들여서 새로 파일을 만듬

주의점

1. 파일명 충돌 주의

파일 업로드 시에는 중복 파일명이 없어야 합니다. 따라서 파일명이 충돌되지 않는 기본명칭을 따로 합니다.

웹서버에 올려놓고 파일명만 DB에 저장

-> DB컬럼이 2개 : originalfilename, newfilename

충돌이 안 되려면 time에 따라 이름을 지어놓는 경우가 많습니다.

system time은 34424234 이런 식으로 되어있어서, 계속 늘어나게 되어있습니다. 따라서 충돌될 가능성이 거의 없습니다.

originalfilename : 사용자가 저장한 파일 이름

newfilename : time(sus) 34424234 

 

파일 다운로드시에는 오리지널 파일명으로 다운받게끔 설정해야 합니다.

 

 

썸머노트 참고하기 : 이미지까지 같이 올릴 수 있는거

https://programmer93.tistory.com/27

 

Summernote 사용법 , 썸머노트 사용법 - 삽질중인 개발자

- 썸머노트사용법 정리 - 홈페이지에서 글을 쓰는 부분에 일반 텍스트 형태의 글 쓰기만 지원한다면 사용자가 사용하기 불편하다. 이러한 점 때문에 대부분의 홈페이지에서는 웹에디터를 지원

programmer93.tistory.com

 

multipartResolver

파일은 문자열이 아니라 byte 형이다

byte형과 모든걸 같이 묶어서 보내는것

 

enctype 

블로그 참조 : https://blogpack.tistory.com/1088

 

HTML 폼(<form>)의 enctype 속성과 multipart/form-data 의 의미

HTML 폼을 POST 방식으로 전송할 때는 전송하는 데이터를 인코딩하기 위해 인코딩 타입에 대한 명시가 필요합니다. 인코딩 타입을 명시하는 속성은 "enctype"입니다. "enctype" 은 3가지 속성 값을 사용

blogpack.tistory.com

블로그참조 2: https://tibang.tistory.com/entry/form%ED%83%9C%EA%B7%B8%EC%9D%98-enctype-%EC%86%8D%EC%84%B1

 

form태그의 enctype 속성

파일을 업로드 할때 태그에서 ENCTYPE="multipart/form-data"라는 애트리뷰트를 반드시 써야 한다. 그렇게 하지 않으면 웹 서버로 데이터를 넘길때 파일의 경로명만 전송되고 파일 내용이 전송되지 않기

tibang.tistory.com

HTML 폼을 POST 방식으로 전송할 때는 전송하는 데이터를 인코딩하기 위해 인코딩 타입에 대한 명시가 필요합니다.

인코딩 타입을 명시하는 속성은 "enctype"입니다.

 

파일을 업로드 할때 <form>태그에서 ENCTYPE="multipart/form-data"라는 애트리뷰트를 반드시 써야 합니다.

그렇게 하지 않으면 웹 서버로 데이터를 넘길때 파일의 경로명만 전송되고 파일 내용이 전송되지 않기 때문입니다.

그리고 이때 METHOD 애트리뷰트에는 'POST' 값을 지정해야 합니다.

<form action="pdsupdateAf.do"  method="post" enctype="multipart/form-data">

 

"enctype" 은 3가지 속성 값을 사용할 수 있습니다.

1. application/www-form-urlencoded

디폴트값 - enctype을 따로 설정하지 않으면 이 값이 설정된ㄷ합나더, 폼데이터는 서버로 전송되기 전에 URL-Encode 이 됩니다. 인코딩 속성(enctype)을 명시하지 않은 POST 방식 폼 전송에 기본 값으로 사용됩니다.

 

2. multipart/form-data

파일이나 이미지를 서버로 전송할 경우 이 방식을 사용합니다.

 

3. text/plain

이 형식은 인코딩을 하지 않은 문자 상태로 전송합니다. 3개를 제외한 나머지 인코딩 속성 값은 무시됩니다.

개발용 외에는 사용하지 않는 것이 좋습니다.

인코딩 없이 문자열을 그대로 전송하기 때문에 보안성이 전혀 없으며, 한글 등 2바이트 문자는 전송 후 글자가 깨지는 현상이 발생해 폼 데이터를 처리하는 페이지에서 정상적으로 데이터를 읽지 못할 수도 있습니다.

 

 

input type='file' 

블로그 참조 : https://hianna.tistory.com/346

input type을 file로 설정하면 [파일선택] 버튼이 자동으로 생깁니다.

<table border="1">
<tr>
	<th>아이디</th>
	<td>
		<%=login.getId() %>
		<input type="hidden" name="id" value="<%=login.getId() %>">
	</td>	
</tr>
<tr>
	<th>제목</th>
	<td>
		<input type="text" name="title" size="50">
	</td> 
</tr>
<tr>
	<th>파일업로드</th>
	<td>
		<input type="file" name="fileload">
	</td>
</tr>
<tr>
	<th>내용</th>
	<td>
		<textarea rows="10" cols="50" name="content"></textarea>
	</td>
</tr>
<tr>
	<td colspan="2">
		<input type="submit" value="자료올리기">
	</td>
</tr>
</table>

 

 

파일 업로드 

자료추가 버튼을 누르고 제목, 내용 입력후 파일선택 버튼을 눌러 파일을 선택합니다.

자료올리기를 클릭해야 업로드가 완료됩니다. 

 

1. util 파일에 파일명을 time으로 변경하는 기능을 구현합니다.

2. mapper-service-dao-controller 순으로 파일을 만듭니다.

@PostMapping(value = "pdsupload.do")
	public String pdsupload(PdsDto dto, 
			
			//pdswrite.jsp에서 fileload라고 업로드 했으므로 value값은 fileload이다.
			//requered = false : 업로드가 안됐을때 다시한번 하지 않겠다.
			//HttpServletRequest : upload경로 얻어오기 위해 함수 아무데나 적어준다.
							@RequestParam(value = "fileload", required = false)
							MultipartFile fileload,
							HttpServletRequest req) {	// 업로드경로 설정하기 위한 목적으로 추가됨
		
		// filename 취득
		// filename은 fileload가 갖고있다. pdswrite.jsp에서 name=fileload이므로
		String filename = fileload.getOriginalFilename();	// getOriginalFilename : 원본의 파일명
		//getOriginalFilename 가 null이면 자료 안 올린 것!
		
		dto.setFilename(filename);	// filename: 원본 파일명(DB) -> DB에만 저장
		//dto에서 filename이 오리지날 파일명 Newfilename이 우리가 만든 파일명
		// 오리지날 파일명에다 filename을 넣어준다. = 데이타베이스에 넣는작업.
		
		// upload의 경로 설정
		// 방법1. server - server에 올리면 서버 껐다 킬때마다 없어질 가능성도 있다.
		String fupload = req.getServletContext().getRealPath("/upload"); //upload폴더 지정
		
		// 방법2. 폴더 - 폴더에 올리면 없어질 가능성 없음. (지금은 사용x)
	//	String fupload = "c:\\temp";
		
		System.out.println("fupload:" + fupload); //저장된 경로 확인용
		
		// 파일명이 충돌되지 않는 명칭(Date)으로 변경 -> 미리 PdsUtil에 구현해놨음
		String newfilename = PdsUtil.getNewFileName(filename);
		
		dto.setNewfilename(newfilename);	// 변경된 파일명 - 실제로 올라가는 파일명
		
		
		//파일 하나 만들어서 그 파일에 데이터 전송하는 작업
		File file = new File(fupload + "/" + newfilename); //파일 업로드 경로도 같이
					//           경로/실제 올라가는(충돌되지 않는) 파일명
						
		try {
			// 실제로 파일 생성 + 기입 = 업로드
			//FileUtils : io소속
			FileUtils.writeByteArrayToFile(file, fileload.getBytes());
			
			// db에 저장
			service.uploadPds(dto);
			
		} catch (IOException e) {			
			e.printStackTrace();
		}		
		
		return "redirect:/pdslist.do";
	}

 

<코드 해석>

FileUtils.writeByteArrayToFile(file, fileload.getBytes());

이 코드는 Apache Commons IO 라이브러리의 FileUtils 클래스를 사용하여 바이트 배열(byte array)을 파일로 쓰는 기능을 수행합니다.

해당 메서드인 writeByteArrayToFile(File file, byte[] data)은

첫 번째 매개변수로 파일 객체(File)를 받고, 두 번째 매개변수로는 바이트 배열(byte array)을 받습니다.

메서드는 두 번째 매개변수로 받은 바이트 배열을 첫 번째 매개변수로 받은 파일 객체에 씁니다.

따라서 위 코드에서는 file 변수가 가리키는 파일에 fileload 문자열을 바이트 배열로 변환한 값을 씁니다.

fileload.getBytes()는 문자열 fileload를 바이트 배열로 변환한 값을 반환하는 메서드입니다.

 

파일 다운로드

업로드와 다른점

업로드는 자료추가 버튼을 누르고 제목, 내용 입력후 파일선택 버튼을 눌러 파일을 선택합니다. 자료올리기를 클릭해야 업로드가 완료됩니다. 반면, 다운로드를 할 때는 "다운로드"버튼을 클릭했을 때 이동하지 않고 제자리에서 다운만 돼야합니다.

 

만들기 순서

1. util 파일에 파일명을 time으로 변경하는 기능을 구현합니다. 

 

<코드 해석>

public class DownloadView extends AbstractView{

위 코드는 AbstractView 클래스를 상속하는 DownloadView 클래스를 정의하는 코드입니다.

public class DownloadView :  DownloadView 클래스의 정의를 시작하는 키워드입니다.

extends AbstractView :  DownloadView 클래스가 AbstractView 클래스를 상속한다는 것을 나타냅니다.

AbstractView : Spring Framework에서 제공하는 추상 클래스 중 하나로, Spring MVC에서 View를 생성하기 위해 사용됩니다.

DownloadView 클래스 :  AbstractView 클래스를 상속하여 이를 확장하고, 파일 다운로드를 위한 View를 생성하는 기능을 구현합니다.

즉, DownloadView 클래스는 Spring MVC에서 View를 생성하는 기본적인 뼈대를 상속받고, 이를 커스터마이징하여 파일 다운로드를 위한 View를 만드는 것입니다. 이를 통해 개발자는 파일 다운로드 기능을 구현하는 데 필요한 많은 작업을 자동화하고, 코드의 재사용성을 높일 수 있습니다.

 


	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		System.out.println("DownloadView renderMergedOutputModel");

위 코드는"다운로드"버튼을 클릭했을 때 이동하지 않고 제자리에서 다운만 되게 만드는 코드입니다.

뷰 위에 윈도우가 하나 만들어지면서 그 윈도우에서 작업을 하는데, 위 코드를 통해 작업을 합니다.

위 코드는 DownloadView 클래스에서 구현된 renderMergedOutputModel() 메서드를 오버라이딩하는 코드입니다.

renderMergedOutputModel() 메서드는 AbstractView 클래스의 추상 메서드이며, Spring MVC에서 View가 생성될 때 호출되어 컨트롤러에서 전달된 모델 데이터를 이용하여 실제로 View를 생성하는 역할을 합니다.

따라서, DownloadView 클래스에서는 renderMergedOutputModel() 메서드를 오버라이딩하여 파일 다운로드를 위한 View를 생성합니다.

@Override 어노테이션은 메서드가 상위 클래스나 인터페이스에서 정의된 메서드를 오버라이드하고 있음을 나타내는 표시입니다.

renderMergedOutputModel() 메서드는 Map<String, Object> model, HttpServletRequest request, HttpServletResponse response 세 가지 매개변수를 받습니다.

 

model은 컨트롤러에서 전달된 모델 데이터를 가지고 있는 Map 객체이며, request와 response는 HTTP 요청과 응답에 대한 정보를 가지고 있는 객체입니다.

 

System.out.println("DownloadView renderMergedOutputModel");는 파일 다운로드 View가 생성될 때 호출되어지는 메서드라는 것을 확인하기 위한 디버깅 코드입니다. 실제 파일 다운로드를 위한 코드는 이후에 구현되는 것으로 예상됩니다.


		<!-- download -->
	<bean id="downloadView" class="mul.cam.a.util.DownloadView"></bean>
	<!-- DownloadView downloadView = new DownloadView(); -->
	
    //창을 띄워주는 부분
	<bean id="downloadViewResolver"
    	class="org.springframework.web.servlet.view.BeanNameViewResolver"> //자동 뷰 띄워주기
		<property name="order">
			<value>0</value>
		</property>	
	</bean>

위 코드는 Spring Framework의 빈(bean) 설정 파일(file-context.xml)에 등록된 DownloadView 클래스와 BeanNameViewResolver 클래스를 이용하여 파일 다운로드 View를 생성하고 설정하는 코드입니다.

<bean id="downloadView" class="mul.cam.a.util.DownloadView"></bean>은 DownloadView 클래스를 빈으로 등록하는 코드입니다. 이렇게 등록된 빈은 스프링에서 필요에 따라 인스턴스를 생성하고, 빈으로 등록된 클래스를 참조할 수 있게 됩니다.

<bean id="downloadViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">은 BeanNameViewResolver 클래스를 빈으로 등록하는 코드입니다. BeanNameViewResolver 클래스는 뷰 이름과 일치하는 이름을 가진 빈 객체를 찾아 뷰로 사용하는 클래스입니다.

<property name="order"><value>0</value></property>는 뷰 리졸버가 여러 개인 경우에 각각의 우선순위를 지정하는 코드입니다.

order 속성은 뷰 리졸버의 우선순위를 설정하는 속성으로, 작은 숫자일수록 우선순위가 높습니다.

위 코드에서는 downloadViewResolver가 가장 높은 우선순위(0)를 가지도록 설정하였습니다.

따라서, 위 코드는 DownloadView 클래스를 빈으로 등록하고, BeanNameViewResolver 클래스를 이용하여 등록된 빈을 뷰로 사용하는 설정을 하는 것입니다. 이를 통해 파일 다운로드 기능을 구현하는 데 필요한 View를 등록하고 사용할 수 있습니다.

 


파일 이동 경로 보기 

1번코드 : controller.java 
	@PostMapping(value = "filedownLoad.do")
					//pdslist.jsp에서 seq, filename, newfilename 보냄
	public String filedownLoad(int seq, String filename, String newfilename, Model model, 
				HttpServletRequest req) { // 업로드경로 설정하기 위한 목적으로 추가됨


	String fupload = req.getServletContext().getRealPath("/upload"); //upload폴더 지정
				
	File downloadFile = new File(fupload + "/" + newfilename);	
					
		
		model.addAttribute("downloadFile", downloadFile);	// file 	
		model.addAttribute("filename", filename);		// string 					
		model.addAttribute("seq", seq);			// int 
		
		return "downloadView"; 
	}

2번코드 : file-context.xml 
	<bean id="downloadView" class="mul.cam.a.util.DownloadView"></bean>

	
	<!--  창을 띄우는 부분 -->
	<bean id="downloadViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
		<property name="order">
			<value>0</value>
		</property>	
	</bean>
	
</beans>

3번코드 : DownloadView.java
public class DownloadView extends AbstractView{	
	//DownloadView 클래스는 Spring MVC에서 View를 생성하는 기본적인 뼈대를 상속받고, 
	//이를 커스터마이징하여 파일 다운로드를 위한 View를 만드는 것

	//다운로드 할 수 있는 뷰(안 보이는) 만들었음.
	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		System.out.println("DownloadView renderMergedOutputModel");
		
	//위의 뷰를 등록하려면 file-context 에 download 부분 추가하기
		
		File downloadFile = (File)model.get("downloadFile");
		String filename = (String)model.get("filename");	// 원본 파일명
		int seq = (Integer)model.get("seq");
		
		response.setContentType(this.getContentType());
		response.setContentLength((int)downloadFile.length());
		
		
		filename = URLEncoder.encode(filename, "utf-8");
		
		
		response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\";");
		response.setHeader("Content-Transfer-Encoding", "binary;");
		response.setHeader("Content-Length", "" + downloadFile.length());
		response.setHeader("Pragma", "no-cache;"); 
		response.setHeader("Expires", "-1;");
		
		OutputStream os = response.getOutputStream();
		FileInputStream fis = new FileInputStream(downloadFile);
		
		// 실제 데이터 기입
		FileCopyUtils.copy(fis, os);
		
		// down load count 증가
		
		if(fis != null) {
			fis.close();
		}
	}

호출 순서는 controller > file-context.xml > donwloadview.java 입니다.

controller : 다운로드할 파일과 관련된 정보를 Model 객체에 추가하고 "downloadView" 뷰 이름을 반환합니다.

file-context.xml :"downloadView" 빈을 생성하여 View 객체를 생성하고, 이때 controller 코드에서 반환한 "downloadView" 뷰 이름을 참조하여 해당 뷰를 생성합니다.

 

Downloadview.java : 이렇게 생성된 View 객체에서는 Downloadview.java에서 정의한 DownloadView 클래스의 renderMergedOutputModel 메서드를 호출하여 파일 다운로드를 처리합니다.

리뷰

먼저 1번 코드에서 파일 다운로드를 위한 정보들을 Model 객체에 추가하고, "downloadView"라는 뷰 이름을 반환합니다.

2번 코드에서는 "downloadView" 라는 이름의 빈(Bean)을 등록하며, 이 빈은 DownloadView 클래스를 참조합니다.

1번에서 반환된 "downloadView" 뷰 이름은 2번에서 등록한 ViewResolver에서 찾아서 해당 뷰를 생성합니다.

이때 생성된 View 객체에서는 3번 코드에서 정의한 DownloadView 클래스의 renderMergedOutputModel 메서드를 호출하여 파일 다운로드를 처리합니다.

즉, 1번 코드에서 시작하여 2번 코드에서 "downloadView" 빈을 등록하고, 1번 코드에서 반환한 "downloadView" 뷰 이름을 참조하여 해당 뷰를 생성합니다. 이때 생성된 View 객체에서는 3번 코드에서 정의한 DownloadView 클래스의 renderMergedOutputModel 메서드를 호출하여 파일 다운로드를 처리합니다.

 

1번 코드인 Controller.java 파일에서 filedownLoad 함수는 "downloadView"라는 뷰를 반환합니다.

이 뷰 이름은 파일 다운로드를 처리하는 DownloadView 클래스를 참조하도록 설정되어 있습니다.

따라서 "downloadView"는 DownloadView 클래스의 renderMergedOutputModel 함수에서 실제 파일 다운로드를 수행하도록 합니다.

 

2번 코드인 file-context.xml 파일은 Spring MVC에서 ViewResolver를 구성하고 있습니다.

downloadViewResolver라는 BeanNameViewResolver가 설정되어 있으며, 이는 "downloadView"라는 뷰 이름을 가진 클래스를 찾아서 해당 클래스를 사용하여 뷰를 생성하도록 설정되어 있습니다.

 

3번 코드인 DownloadView 클래스는 AbstractView 클래스를 상속받아 View를 생성하는 기본적인 뼈대를 구현하고 있습니다. renderMergedOutputModel 함수에서 실제 파일 다운로드를 수행하고 있습니다.

따라서 Controller.java 파일에서 반환한 "downloadView" 뷰는 DownloadView 클래스의 renderMergedOutputModel 함수를 호출하게 됩니다.


		//응답형으로 세팅하기
		1. response.setContentType(this.getContentType());
		2. response.setContentLength((int)downloadFile.length());

HTTP 응답 헤더의 Content-Type과 Content-Length를 설정하는 코드입니다.

 

1. response.setContentType(this.getContentType())는 HTTP 응답 헤더에 Content-Type을 설정하는 코드입니다.

Content-Type은 HTTP 요청/응답에서 전송되는 미디어 타입을 나타내며, 다운로드할 파일의 타입에 따라 설정됩니다.

 

getContentType()은 현재 View 객체의 ContentType을 반환하는 메서드입니다. View 객체는 HTTP 응답으로 반환되는 데이터의 형식을 결정하는 역할을 합니다. ContentType은 이 데이터의 MIME 유형을 지정하는 데 사용됩니다. 예를 들어, 이미지 파일의 ContentType은 "image/jpeg", "image/png" 등과 같이 이미지 파일의 MIME 유형에 따라 결정됩니다. 이 메서드는 DownloadView 클래스에서 오버라이딩되어 파일 다운로드에 필요한 ContentType을 반환하도록 구현되어 있습니다.

 

response.setContentType(this.getContentType())에서는 해당 응답에 대한 MIME 타입을 설정합니다. getContentType() 메소드는 해당 View 객체의 ContentType 속성을 반환하며, DownloadView 클래스에서는 "application/octet-stream" 값을 반환하도록 설정되어 있습니다. 이는 다운로드 파일과 같이 어떤 특정한 MIME 타입이 없는 경우에 사용되는 일반적인 값입니다.

 

2. response.setContentLength((int)downloadFile.length())에서는 응답 본문의 길이를 설정합니다. downloadFile.length() 메소드는 다운로드할 파일의 크기를 반환하며, (int)로 강제 형변환하여 int 형으로 변환한 후 setContentLength() 메소드를 호출하여 해당 응답의 Content-Length 헤더를 설정합니다. 이는 클라이언트가 다운로드할 파일의 크기를 미리 알 수 있도록 하기 위한 것입니다.


	OutputStream os = response.getOutputStream(); //생성
		//파일 흐름 입력
		FileInputStream fis = new FileInputStream(downloadFile);
		
		
		// 실제 데이터 기입 (파일 다운로드 전에)
		FileCopyUtils.copy(fis, os);
		
		// down load count 증가
		
		if(fis != null) {
			fis.close();
		}

위 코드는 파일 다운로드를 처리하는데 사용되는 메서드 중 일부입니다.

OutputStream os는 응답으로 보내질 파일 데이터를 쓰기 위한 출력 스트림을 생성하는 코드입니다.

FileInputStream fis는 다운로드하려는 파일을 읽기 위한 입력 스트림을 생성하는 코드입니다.

FileCopyUtils.copy() 메서드는 fis로부터 읽은 파일 데이터를 os로 복사하여 실제로 파일 다운로드를 수행하는 코드입니다.

마지막으로, if (fis != null) 구문은 파일 입력 스트림 fis가 null이 아닐 경우에만 close() 메서드를 호출하여 해당 스트림을 닫아줍니다.

 

 

댓글