솔직히 살 때부터 마우스 휠이 조금 이상했다. 근데 1년 넘고 부터는 굉장히 심각했다 마우스 휠을 내리면 올라가고... 처음에는 참고 쓸만 했는데 무상 A/S가 지나니 점점 나빠졌다. 소프트웨어로 휠 다운을 높여서 내릴 때 올라가는 것보다 내리는게 더 커서 쓰고 있었다. 근데 화가 나서 큰맘먹고 A/S 보내기로 결정... 과정이 좀 많이 짜증나서 적었다. 그런데 결과는 만족...ㄷㄷ;; 이렇게 쓰면 COX에서는 내가 누군지 알겠네... 

 

1. 전화 접수

전화했다. 앱코(1644-0844)에 전화해야 한다. 전화하니깐 10분 정도 대기했다. 연결되니깐 증상이랑 번호 등등 적어서 같이 보내라고 하더라. 물론 택배비 선불... 전화 할때마다 기본 5분 대기다.

 

 

2. 입고

21/7/12 보냈다. 14일날 입고 됐다고 문자가 왔다. 그러면서 처리 소요 2~3일 예정이라고 문자가 왔다.

 

3. 접수

14일날 입고가 됐지만 그 다음주인 19일날 접수 되어서 무상 A/S 기간인지 체크해야되서 이것저것 하고 A/S 비용 문제를 처리했다. A/S 비용 14000원 택배비 2500원 16500원을 입금해야한다. 내가 먼저 선불 택배 보냈기 때문에 총 19000원이 들었다. 사실상 입고 처리 및 접수에만 1주일 걸렸다. 얼마나 걸리냐고 물어보니 바로 비용처리되면 이번주 안에는 받을 수 있을 거라고 말했다.

 

4. A/S 

19일날 비용처리가 끝났기 때문에 A/S를 기다리면된다. 근데 26일 즉 일주일이 지나도 연락이 없어 오전에 전화를 했다. 역시나 6분 기다렸다. 언제되냐고 물어봤다. 무슨 이전에 PC방 A/S가 대량으로 들어왔다며 변명을 했다. 문제는 내가 먼저 접수가 됐다면 내가 먼저 처리 되는게 맞는게 아닌가... 물어보진 않았는데 대화의 맥락 상 내가 먼저 같긴 했다. 왜냐면 비용처리할 때 얼마나 걸리냐고 물었을 때 그런 얘기도 없었고 생각한 것보다 너무 늦어졌다. 아무튼 전화받은 사람이 이거 들어온지 오래됐으니 빨리 처리해달라고 문의하겠다고 한다. 

 

그로부터 오후 2시쯤 출고 처리 됐다고 한다.

 

5. A/S 결과물 

맞교환으로 보인다. 물론 리퍼제품 맞교환일 수도 있다. 왜냐면 표면에 흰색 긁힘자국은 아닌데 묻어있었다. 그래도 새 박스에 제품이 들어있는 것을 본 나는 화가 수그러들었다...

 

결과적으로 A/S 결과가 좋았다. 휠도 잘되고 마우스도 깔끔하고 아까 흰색 긁힘자국 이런거 얘기했는데 사진에선 안보이는데... 내가 닦았다... 2주 기다리고 맞교환 흠... 난 만족스럽긴한데 누군가는 별로일수도?

 

파일을 다루기 때문에 업로드할 파일이나 다운로드할 파일의 관련 설정을 먼저 해야한다(업로드 경로, 업로드 크기 설정, 다운로드 파일 크기 등...) 프로젝트 안에 application.properties나 application.yml이 있을 것이다. 관련 설정 포함 시켜주자.

 

1. 설정

spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=2KB
spring.servlet.multipart.max-file-size=200MB
spring.servlet.multipart.max-request-size=215MB
upload-path=/User/upload

이때 주의할 점은 업로드 경로다. 절대주소에서 최상위 디렉토리(Root)만 빠진 주소다. 최상위 디렉토리를 프로젝트 폴더가 C에 있나 D에 있나로 결정된다. 그래서 프로젝트 폴더가 C/D 드라이브 등 잘 파악하고 하자. 

 

2. Controller - 파일 업로드 , 파일 멀티 업로드, 파일 다운로드 3개로 구성된다.

가끔 다운로드가 안되는 경우가 있다. 코드에 문제는 아니다. 다만 설정을 잘못했을 것이다...

RequestMapping 주소가 있기 때문에 파일 업로드시 다운로드 Uri에 RequestMapping이 추가 됐는지 확인하자.

package com.example.filedemo.controller;

import com.example.filedemo.domain.FileUpload;
import com.example.filedemo.service.FileStorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/file")

public class FileController {

    private static final Logger logger = LoggerFactory.getLogger(FileController.class);

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private FileService fileService;
    
    @PostMapping("/uploadfile")
    public File uploadFile(@RequestParam("file") MultipartFile file) {
        if(file.isEmpty()){
            //파일 업로드가 안됐을 시 처리
        }
        String fileName = fileStorageService.storeFile(file);
        //확장자만 추출하는 형태 ex) exe , png, jpg ...
        String fileExt = fileName.replaceAll("^.*\\.(.*)$", "$1");
        
        String fileOriginalName = StringUtils.cleanPath(file.getOriginalFilename());

        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                .path("/file/downloadfile/")
                .path(fileName)
                .toUriString();

        return new File(fileName, fileOriginalName, fileExt, fileDownloadUri);
    }
    
	@PostMapping("/uploadmultiplefiles")
    public List<File> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
        return Arrays.asList(files)
                .stream()
                .map(file -> uploadFile(file))
                .collect(Collectors.toList());
    }
    
	@GetMapping("/downloadfile/{fileName:.+}")
    public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) {
        // Load file as Resource
        Resource resource = fileStorageService.loadFileAsResource(fileName);

        String contentType = null;
        try {
            contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
        } catch (IOException ex) {
            logger.info("Could not determine file type.");
        }

        // Fallback to the default content type if type could not be determined
        if(contentType == null) {
            contentType = "application/octet-stream";
        }

        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType(contentType))
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    }
}

 

3. 파일 업로드에 대한 Response 해줄 클래스다. DTO 쯤으로 생각한다. 별 내용없고 파일 이름, 다운로드 경로, 사이즈 등등...을 저장하는 클래스다. 아마 경우에 따라 필요하지 않아서 생성하지 않을수도 있지만 이번에는 생성하는 방식으로 개발했다.

package com.example.filedemo.domain;

@Getter
@Setter
@AllArgsConstructor
@ToString
public class FileUpload {
    private String fileName;
    private String fileOriginalName;
    private String fileDownloadUri;
    private String fileExt;
    private long size;
    
    public FileUpload(String fileName, String fileOriginalName, String fileExt, String fileDownloadUri) {
        this.fileName = fileName;
        this.fileOriginalName = fileOriginalName;
        this.fileExt = fileExt;
        this.fileDownloadUri = fileDownloadUri;
    }
}

4. 서비스

 

이제 직접적으로 파일 업로드/다운로드의 서비스 로직을 만들 차례다. 

만약 맨 처음 파일 경로가 안 읽어질수도 있다. 주의하자.

파일 업로드 시 파일 이름 등을 바꾸고 싶다면 storeFile()을 보면 된다.

보통 파일이름을 오리지널 그대로 올리는 경우가 있는데 그렇게 해선 안된다. 이름이 겹치면 올라가지 않을뿐더라 나중에 보안에 문제가 생길 수 있다. 원래 파일이름을 통신을 통해 보내서 DB에 저장하고 파일이름 설계에 의한 이름 + 타임스태프로 해주는 경우가 많으니 참고바란다. 우선 난 타임스탬프 + _파일이름으로 해놓을 예정이다. 물론 Response에서 오리지널 이름과 변환 이름 둘 다 반환한다.

package com.example.filedemo.service;

import com.example.filedemo.property.config.Properties;
import com.example.filedemo.exception.FileStorageException;
import com.example.filedemo.exception.MyFileNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

@Service
public class FileStorageService {

    private final Path fileStorageLocation;

    @Autowired
    public FileStorageService(Properties Properties) {
        this.fileStorageLocation = Paths.get(Properties.getUploadPath())
                .toAbsolutePath().normalize();

        try {
            Files.createDirectories(this.fileStorageLocation);
        } catch (Exception ex) {
            throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
        }
    }

    public String storeFile(MultipartFile file) {
        SimpleDateFormat sdf = new SimpleDateFormat ("yyyyMMddhhmmss_");
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        String timeStamp = sdf.format(timestamp);
        
        String fileName = timeStamp + StringUtils.cleanPath(file.getOriginalFilename());

        try {
            // Check if the file's name contains invalid characters
            if(fileName.contains("..")) {
                throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
            }

            // Copy file to the target location (Replacing existing file with the same name)
            Path targetLocation = this.fileStorageLocation.resolve(fileName);
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);

            return fileName;
        } catch (IOException ex) {
            throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
        }
    }

    public Resource loadFileAsResource(String fileName) {
        try {
            Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
            Resource resource = new UrlResource(filePath.toUri());
            if(resource.exists()) {
                return resource;
            } else {
                throw new MyFileNotFoundException("File not found " + fileName);
            }
        } catch (MalformedURLException ex) {
            throw new MyFileNotFoundException("File not found " + fileName, ex);
        }
    }
}

 

5. 예외 클래스

 

파일을 업로드나 다운로드할 때 예외가 발생을 경우를 처리해 줄 예외클래스를 만들어준다.

package com.example.filedemo.exception;

public class FileStorageException extends RuntimeException {
    public FileStorageException(String message) {
        super(message);
    }

    public FileStorageException(String message, Throwable cause) {
        super(message, cause);
    }
}
package com.example.filedemo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class MyFileNotFoundException extends RuntimeException {
    public MyFileNotFoundException(String message) {
        super(message);
    }

    public MyFileNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

6. 테스트

 

백엔드 개발이 완료 됐으니 API를 Postman으로 테스트를 해보자.

지금 같은 경우 Controller에서 RequestMapping을 해주었다. 그렇기 때문에 주소가 /localhost:8080/api/file/uploadfile이 된다.

'IT > Spring' 카테고리의 다른 글

[Spring Boot] Spring Boot 입문 - 프로젝트 세팅  (0) 2021.06.22
[Spring] Spring Framework 이클립스 세팅  (0) 2021.06.03

서버 구축 이후에 FTP로 문제가 많았다. 그 중 하나가 FTP Write가 안된다.

 

550 Permission denied. 뭐 대충 에러 메시지 보니깐 권한 없다고 나온다. 이럴 경우 폴더에 대한 쓰기 권한이 없거나 FTP 자체 쓰기 권한이 없거나 둘 중 하나 같다.

 

그래서 이전에 폴더 권한 문제를 봤으니 이번에는 FTP 쓰기 권한을 봐야될 것 같다.

 

먼저 vsftpd.conf 파일을 들여다 보자 기본적으로 시스템 파일은 root 권한을 사용해야 한다. 안 그러면 수정이 안된다.

sudo vi /etc/vsftpd.conf  

 

그러면 많은 주석 중 #write enable=YES 이게 있을 것이다. 가볍게 주석을 해제해주자

 

그리고 서비스 재시작하면 내 경우는 바로 쓰기가 가능했다.

sudo /etc/init.d/vsftpd restart

그리고 회사 AWS EC2 백업 도중 파일을 업로드 해야되는 경우가 생겼다. 그래서 올리는데 에러가 발생한다.

open for write: permission denied

현재 유저는 해당 폴더에 쓰기 권한이 없다는 에러다. 간단하게 명령어로 권한을 주면 된다.

sudo chmod -R 777 /var/www/html

 

급하게 회사에서 외주 프로젝트가 들어왔다. 한번쯤은 스프링으로 해보는 것도 나쁘지 않다고 생각했는데... 너무 두렵다... 아무튼 예전에 egov랑 스프링을 조금 해본 정도로 프로젝트를 진행한다. 개념은 아예 없기 때문에 블로그 쓰면서 스터디 하면서 정리할 예정이다. 너무 쉽게 생각했다...

 

IDE

우선 IDE인데 이클립스를 하려고 했는데 나의 상황이 사용중인 IDE가 안드로이드 스튜디오(안드로이드), VS Code(Node.js) 인데 스프링 하려고 이클립스까지 쓰면 IDE는 총 3개 사용하는 것이다. 아마 PC 바꾸면 대참사가 날 것이다. 그래서 IntelliJ IDE를 사용해보려고 한다. 아마 자기가 학생이면 잘 모를수도 있다.  IntelliJ도 학생은 무료로 사용할 수 있다. 그렇기 때문에 한번 쯤 써보는 것도 좋다. 굉장히 많은 툴들을 지원하고 JAVA, PHP, Node.js, Android...등 많은 언어들을 지원하기 때문에 나로썬 써보는 게 좋다고 생각했다. 그래서 대부분 IDE를 IntelliJ로 진행해보려한다.

 

프로젝트 세팅은 스프링 페이지에서 만들어서 IDE에 Import하는 방법과 IDE에서 생성하는 방법이 있다. 물론 두 방법 다 과정은 같기 때문에 IDE에서 생성해보려고 한다.

 

1. New > project > New Project 에서 왼쪽 탭에 Spring Initalizr을 누르면 위 이미지처럼 나온다. 물론 탭에 없다면 맨 아래 Empty Project에서 찾아보기 바란다...

2. 스프링 프로젝트의 세팅 - Group과 Artifact, package name 등을 입력하고 빌드도구랑 언어는 우선은 Maven, Java를 선택

패키징은 Jar와 War중 선택이 가능한데 Jar를 선택하면 스프링부트 내장톰캣(Embeded Tomcat)을 사용하여 Stand Alone으로 WAS를 구동시키고. 외부 WAS에 Deploy해서 사용하는 환경이시라면 War를 선택하여 내보내면 된단다...


3. Next를 눌러서 다음으로 DI 세팅이다. 내가 필요한 DI를 선택하면 빌드에 자동으로 항목이 추가되고 리포지토리에서 내려받아진다. 물론 나중에 따로 추가도 가능하다.

4. Finish를 누르면 끝난다. 생성된 스프링부트 프로젝트의 구조인데 주요 파일로는 스프링부트 컨테이너를 실행시키기 위한 src폴더내의 main가 있고 resources 폴더에는 web을 선택했기 때문에 html, css, js 등의 정적 컨텐츠가 위치할 static 폴더가 자동으로 생성됐으며 여러가지 설정 정보를 위한 application.properties 파일도 생성됐다. 

 

5. 우선 국룰을 통해서 프로젝트를 만들었으면 실행을 해야한다고 생각한다. 이클립스 자바 할 때처럼 Run을 해보자...당연히 바로 안된다. 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-06-25 16:08:48.400 ERROR 238940 --- [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).


Process finished with exit code 0

 

뭐라고 주절주절 하는데 아래 Action: 을 보면 뭐라 써있다. 대충 읽어보면 아까 DI 세팅할 때 DB를 같이 추가 했는데 DB에 대한 기본 설정을 하지 않아 발생한 문제다.

2가지 방법으로 해결했다. 

 

 

5-1 application.properties에 DB 세팅을 해준다. 자신의 설정에 맞게 해주면 된다.

5-2 우선 어노테이션을 통해 Auto Configuration에서 DataSource 관련 설정 로드를 WAS 구동시 하지 않도록 exclude하는 방법이 있다.

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class}) (붙여서 하면 자꾸 클래스 부분이 주석처리 되어 에러나니깐 @SpringBootApplication을 엔터 몇번 치고 복붙하자)

6. 그러면 이제 세팅이 완료 됐으면 빌드 후 다시 실행해보면 정상적으로 작동한다. 맨 아래 Started DemoApplication in 2.162 seconds (JVM running for 3.37) 나온다면 정상 작동을 한 것이다. 

7. 그럼 정말 정상 작동하는지 브라우저에서 확인을 해봐야 하지 않나... 우선 index.html을 간단하게 만들어서 테스트해보자.

리소스 정적 폴더에 index.html을 만들면 바로 intellij에서는 해당하는 html 파일을 바로 브라우저를 킬 수 있게 되어 있지만 브라우저에서 https://localhost:8080에 접속하면 index.html에 작성한 페이지가 나온다. 물론 internet explorer은 https가 안된다. 주의하자!

 

 

'IT > Spring' 카테고리의 다른 글

[Spring Boot] 파일 업로드 / 다운로드 REST API  (0) 2021.07.13
[Spring] Spring Framework 이클립스 세팅  (0) 2021.06.03

이 포스팅에선 postman을 이용하여 업로드를 진행할 것이다. 추후에 안드로이드 어플을 통해서 업로드 Client 부분을 작성할 예정이며 작성하면 이 글을 수정하여 링크를 달아놓을 예정이다.

 

우선 파일을 업로드하여 서버 upload라는 폴더 안에 저장할 것이고, DB에 파일에 대한 정보를 넣을 생각이다. 이 이유는 맨 아래에 남겨놨다.

 

우선 NPM 에서 사용하는 기본 예제다.

var express = require('express')
var multer  = require('multer')
var upload = multer({ dest: 'uploads/' })
 
var app = express()
 
app.post('/profile', upload.single('avatar'), function (req, res, next) {
  // req.file is the `avatar` file
  // req.body will hold the text fields, if there were any
})
 
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
  // req.files is array of `photos` files
  // req.body will contain the text fields, if there were any
})
 
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
  // req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
  //
  // e.g.
  //  req.files['avatar'][0] -> File
  //  req.files['gallery'] -> Array
  //
  // req.body will contain the text fields, if there were any
})

코드에서 보면 알 수 있듯이 upload할 때 속성이 다르다. 

.single(fieldname)

이름을 가진 단일 파일을 허용합니다 fieldname. 단일 파일은 req.file. 로 사용하면 된다.

.array(fieldname[, maxCount])

이름이 모두 인 파일 배열을 허용합니다 fieldname. 둘 이상의 maxCount파일이 업로드 되면 선택적으로 오류가 발생합니다 . 파일 배열은 req.files. 로 사용하면 된다.

.fields(fields)

fields 파라미터로 넘어오는 것들을 배열 형태로 저장된다. 파일은 req.files로 사용하면 된다.

 

 

 

NPM에 나와있는 각 파일 정보다.

multer(opts)

업로드된 파일을 Multer에게 어디로 업로드할 지 알려주며 옵션 객체를 하지않았다면 메모리에 저장된다. 보통은 dest 옵션으로 파일 저장 위치를 지정하는데 더 자세하게 제어하고 싶으면 storage를 사용하면 된다.

var  upload  = multer ( {  dest : ' uploads / ' } ) 

 

storage

DiskStorage

말 그대로 로컬 디스크에 저장한다는 의미이며 두 가지 옵션을 사용할 수 있다. 

1. destnation : 파일의 목적지 즉 경로를 설정 할 수 있다.

2. filename : 파일 이름을 설정 할 수 있다. 아래 코드에서 보면 알 수 있듯이 Date.now()가 들어 갔는데 타임스태프를 넣은 이유가 파일 이름이 겹칠 수 있기 때문이라고 생각해두면 될 것 같다.

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, '/tmp/my-uploads')
  },
  filename: function (req, file, cb) {
    cb(null, file.fieldname + '-' + Date.now())
  }
})
 
var upload = multer({ storage: storage })

 

MemoryStorage

메모리 스토리지 엔진은 파일을 메모리에 Buffer개체 로 저장 합니다. 옵션이 없습니다.

var storage = multer.memoryStorage()
var upload = multer({ storage: storage })

 

 

파일 정보를 DB에 저장해둬야 할 필요가 있다. 왜냐면 파일을 다운 받을 경우 자신이 업로드한 파일을 정확하게 받을 수 있다. 그리고 파일 이름은 한글로 넣을 경우 깨지는 경우가 발생했다. UTF-8 등 ... 인코딩 많은걸 해봤지만 안됐다. 나중에 듣기론 이런 경우가 많아 파일 오리지널 이름을 같이 업로드하여 DB에 넣어둔 뒤 다운로드 할 시 DB에서 파일 이름을 꺼내와 다시 붙여주는 걸로 알고 있다. 물론 파일 리스트 조회 할 시도 DB에서 파일 오리지널 이름을 가져와 Client로 넘겨준다.

비동기 다음으로 exports라는 개념이 발목을 잡았다.

지금까지C나 JAVA에서는 include나 import를 하면 그 클래스나 함수, 변수들을 사용할 수 있었다. Node에선 require를 해도 그 변수나 함수를 사용하지 못했다. 

 

Node에서는 모듈에서 exports를 하지 않으면 그 변수나 함수를 사용하지 못하는 것이다.

 

구글링으로 알아본 결과 A라는 모듈을 require를 하면 해당 모듈의 module.exports를 return 한다고 보면 되는 것이다. 그러므로 A모듈에서 module.exports를 하지 않았다면 그 어떤 것도 참조할 수 가 없다.

 

아무튼 본론으로 넘어와서 module.exports를 사용함에 많은 방법이 있었다.

 

우선 module.exports와 exports가 있다. 이 둘의 차이점... 사실 없었다. 공식 문서를 보면 그저 module.exports의 더 짧은 버전... 즉 module 조차도 길었다고 생각했나보다... 아무튼 둘의 차이점은 성능이 아닌 그저 더 짧다는 것에 있다.

exports

Added in: v0.1.12

A reference to the module.exports that is shorter to type. See the section about the exports shortcut for details on when to use exports and when to use module.exports.

 

그렇다면 어떻게 사용하면 되는건가... 여기부터는 솔직히 쓰는 사람의 특성과 그 모듈의 특성에 따라 나뉘는 듯 하다.

 

아래 예를 들어 코드를 작성해봤다. 기호에 맞게 사용하면 될듯하다.

exports.func = function (){console.log('func')}

exports = {func:func}   // 이 경우 따로 func 함수가 정의가 되어 있어야함.

exports = { func : function (){ console.log('func') } }

exports.a = b

 

오랜만에 이클립스 설치하고 JDK 다시 설치하고 하니깐 여간 귀찮다... 스프링도 세팅을 빨리 하고 좀 스터디해서 스프링에 대한것도 올려야겠다.

 

우선 이클립스 키면 스프링을 프로젝트를 생성할 수 없다. 그렇기 때문에 스프링을 추가해줘야한다. 

대부분에 이클립스 플러그인이나 툴들을 Eclipse Marketplace 에서 받으면 편하게 적용할 수 있다.

Help > Eclipse Marketplace 들어와서 스프링을 찾아주면된다.

설치됐는지 확인을 해보면 프로젝트 생성할 때 스프링이 있는지 확인하면 된다.

대학교 이후로는 이클립스를 처음 써본다... 안드로이드도 예전에는 이클립스를 이용했지만 지금은 분리되어 더더욱 쓸일이 없었고 왠만한건 다 VSCODE를 사용하기 때문에 쓸 일이 없었다. 갑자기 스프링을 하게 되어서 급하게 설치를 하는데 그냥 남겨봤다. 이런건 패스해도 되는데...

 

이클립스 홈페이지에서 주황색 다운로드를 누르면 된다.

그러면 exe 파일이 다운이 되면서 실행하면 된다. 실행을 하면 아래 그림 처럼 나올것이다.

우선 첫번째 Eclipse IDE for Java Developers 는 단순히 자바만 사용하는 개발자의 IDE 타입입니다. 그러나 저는 스프링을 사용해야해서 두번째꺼 Eclipse IDE for Enterprise Java and Web Developers 선택을 합니다. 

 

이 다음부터는 설치 위치나 JDK 위치경로 정도만 설정하고 설치하면 됩니다.

 

설치가 다 되면 혹시나 한글이 깨질 걸 생각에서 인코딩만 해보자.

 

메뉴에서 Window > Preferences > general > workspace 로 들어가면 하단에 Text file encoding 을 Other : UTF-8로 바꾼다.

그 다음으로 General > Editors > Text Editors > Spelling 에서 하단에 Encoding 을 Other : UTF-8로 바꿔준다.

+ Recent posts