일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- CRUD
- @Transactional
- React+SpringBoot
- 게시판
- 비전공개발자
- SpringBoot JPA
- 로컬이미지삭제
- React.js
- 로컬이미지불러오기
- jsp
- springboot
- 게시판 CRUD
- 비전공자개발자
- 개발자취업
- 로컬이미지저장
- given when then
- jstl
- Self-invocation
- JPAHibernate
- 게시물수정
- I/OStream
- 테스트코드작성
- mybatis
- React+JPA
- 개발자취업후기
- MySQL
- WebConfig
- 단위테스트코드
- jar빌드
- 게시물상세
- Today
- Total
인텔리가 '되고 싶은' 인텔리재이
'Not allowed to load local resource' 에러 + 로컬 이미지 불러오기 본문
파일 업로드 및 미리보기 기능을 구현하다가 마주한 에러🤣
Chrome 개발자 도구를 통해 다음과 같은 메세지를 확인했습니다.
프로젝트 내부에 저장된 이미지를 불러올때는 이런 에러가 없었는데,
프로젝트 외부(ex. C드라이브)에 저장된 이미지를 불러오려고 하니 다음과 같은 에러가 나타나기 시작했습니다.
원인을 찾아보니, 보안상의 이유로 크롬 브라우저에서 로컬 파일의 접근을 막기 때문이라고 하는데...
그럼 '로컬 파일에 있는 이미지는 영영(?) 브라우저에 불러올 수 없는걸까?'
그건 아니겠지 싶어 이것저것 해본 (aka. 삽질) 기록입니다.📝
Tomcat - server.xml 설정
Tomcat의 설정파일인 server.xml의 내용을 변경하여 해결할 수 있다는 내용이 있어 시도해보았습니다.
server.xml의 위치는 Tomcat 설치 경로 - conf 안에 있습니다.
제 경우에는 C:\Program Files\Apache Software Foundation\Tomcat 9.0\conf 에 있었습니다.
server.xml 하단에 loalhost로 시작하는 부분에 하기와 같은 코드를 추가합니다.
<Context path="/images" docBase="C:\intellij2\files" reloadable="true" />
코드를 저장하고, 톰캣 서버를 재시작해줍니다.
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="/images" docBase="C:\intellij2\files" reloadable="true" />
~~~~ 생략 ~~~~
</Host>
'C:\intellij2\files\파일명.jpeg' 해당 경로에 있는 파일을 브라우저에서 불러올때,
'localhost:포트번호/images/파일명.jpeg' 으로도 동일하게 불러올 수 있다는 것입니다.
docBase(물리적 위치)를 path(논리적 위치)로 포워딩 하는 느낌이라고 보시면 될 것 같습니다.
해당 방법이 검색 시 가장 많이 나오는 방법이기에 많은 기대(?)를 하였지만 결과적으로는 잘 동작하지 않았습니다...😂
*참고 : 주석이더라도 server.xml 파일에 한글을 작성하게되면, 톰캣에서 에러가 난다고 합니다!
WebConfiguration 설정
spring boot 기반의 웹 애플리케이션 서버 실행 중 저장되는 이미지 파일을 특정 폴더 경로에 저장하도록
'WebConfiguration' 을 설정하는 방법을 시도해보았습니다.
1. application.properties 혹은 application.yml 설정 추가
resource.path: file:///C:/intellij2/files/
upload.path: /images/**
- resource.path 는 이미지 파일이 위치한 실제 경로로, 앞에 'files:///' 를 붙여줍니다.
- upload.path 는 /images/ 로 시작하는 요청을 C:/intellij2/files/로 인식합니다.
2. Url 과 Resource 연결하기
WebConfiguration.java 파일을 생성하고 코드를 작성합니다.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
private final String resourcePath;
private final String uploadPath;
// application.yml에 설정한 path를 value에 넣기
public WebConfiguration(@Value("${resource.path}") String resourcePath, @Value("${upload.path}") String uploadPath) {
this.resourcePath = resourcePath;
this.uploadPath = uploadPath;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(uploadPath)
.addResourceLocations(resourcePath);
}
}
- @Configuration : 해당 어노테이션으로 Spring Bean 객체로 등록합니다.
- @Value : 해당 어노테이션으로 application.yml 에 설정해둔 프로퍼티 값을 가져옵니다.
- addResourceLocations에 url 경로를 설정하고, 이를 조작할 path를 addResourceHandler에 설정합니다.
1번은 톰캣 서버 자체의 설정을 변경하여 같은 서버를 사용하는 다른 프로젝트에서도 동일하게 적용이 될 것이기에
1번이 성공하더라도 다른 방법을 찾아야겠다고 생각했지만...
(이유는 알 수 없지만) 제 경우에는 2번 방법으로도 성공하지 못했고, 다음 방법을 찾기로 하였습니다.😂
I/O 스트림 이용
1,2번 방법을 시도하면서 약간 지쳐있는 상태에서 시도한 3번째 방법은, I/O 스트림을 이용하는 방법이었습니다.
브라우저에서 태그를 읽어들일때, <img>의 src를 만나면 속성의 값을 '요청'하고, 이를 백엔드에서 처리해주는 방법입니다.
<img id="itemImg" src="<c:url value='/getImageSrc?fileId='/>' + row.fileList[i].fileId + '">
프론트에서 <img> src에 컨트롤러로 연결될 수 있도록 경로를 잡아주고, 이 때 fileId를 파라미터로 함께 보냅니다.
@GetMapping(value="/getImageSrc")
public void getImageSrc(HttpServletResponse response, @RequestParam(value="fileId") String fileId) throws IOException {
fileService.getImageSrc(response, fileId);
}
- Controller에서 프론트에서 보내줬던 HttpServletResponse, 그리고 파라미터를 @RequestParam 으로 받아줍니다.
- return이 없는 void 메소드의 실행결과는 이를 호출한 View로 전달됩니다.
public void getImageSrc(HttpServletResponse response, String fileId) {
response.setContentType("image/jpeg");
String url = "file:///C:/intellij2/files/";
FileDTO fileDTO = super.getDetailById(fileId, FileDTO.class);
String fileName = fileDTO.getFileName();
try {
URL fileUrl = new URL(url + fileName);
URLConnection connection = fileUrl.openConnection();
connection.setReadTimeout(3000);
InputStream is = new BufferedInputStream(connection.getInputStream());
IOUtils.copy(is, response.getOutputStream());
is.close();
} catch (IOException e) {
System.out.println(e);
}
}
- timeout이나 소켓이 끊겨 외부 url을 읽어오지 못할 수 있는 경우를 대비해 connection을 설정해줍니다.
- fileUrl + fileName으로 InputStream을 생성하고 byte 타입으로 읽어들입니다.
- IOUtils.copy(inputStream, OutputStream) 은 InputStream을 OutputStream으로 손쉽게 복사할 수 있는 기능으로, InputStream으로 읽어들인 내용을 OutputStream에 담아 Controller로 보냅니다.
- InputStream 사용이 끝나면, close()로 닫아줍니다.
Stream이 열려있으면 '사용중인 파일'로 인식되어 파일삭제가 되지 않습니다.
드 디 어! 3번의 방법으로 로컬에 있는 이미지를 불러왔고 추가적으로 저장/삭제하는 기능까지 구현할 수 있었습니다!🙃
해당 기능을 구현하는데 꽤 오랜 시간이 걸렸지만,
이런저런 방법을 고민하는 과정 속에서 조금이나마 성장할 수 있었던 기회였습니다!😊
'✨LEVEL UP🎇 > ERROR' 카테고리의 다른 글
@Transactional 사용 시 Self-invocation 해결 (1) | 2023.07.31 |
---|