SyntaxHighlighter.all(); 'Spring' 카테고리의 글 목록 :: 게을러지고 싶어 부지런한 개발자

나는 IntelliJ를 쓰고 있으므로, 해당 IDE에서 실행하는 방법과 터미널 상에서 실행할 수 있는 방법들을 소개하고자 한다.

예시로, 하나의 애플리케이션을 각 9000, 9001, 9002, 9003 포트로 총 4개를 띄우는 방법을 소개한다.

1. IntelliJ IDE를 통한 실행

- 젤 위에 벌레모양 클릭하여 실행(디버깅모드) 하게 되면 기본 포트는 8080으로 실행된다.

- 포트를 바꿔서 실행하고자 하면 yml파일에 실행되길 원하는 포트를 지정한 뒤 실행하면 된다.

server:
  port: {원하는 포트 번호}

# example
server:
  port: 9000

 

2. Edit Configuration에서 새로운 프로젝트 추가하여 IDE로 실행

  • Edit Configuration → Copy Configuratin → VM 옵션에 -Dserver.port=9002 추가 → IDE로 실행
  • 굳이 port만 바꿔서 실행하고자 한다면, configuration을 복사하여 새로 지정할 필요는 없지만, 나는 하나의 애플리케이션을 동시에 4개의 다른 포트로 실행시키는 방법을 소개하므로, copy configuration을 이용했음

 

 

3. Gradle 을 사용중이므로, gradle 명령어로 터미널에서 실행하기

./gradlew bootrun --args '--server.port=9002'

 

4. jar 파일 실행

- build를 하여, jar파일이 생성되도록 한다.

./gradlew clean build

build가 되면, build 폴더가 생성되고, libs 안에 jar파일 생성된다.

- 생성된 jar파일을 실행한다.    ( java -jar -Dserver.port={실행할포트} {jar파일의 경로}  )

java -jar -Dserver.port=9003 ./build/libs/user-service-0.0.1-SNAPSHOT.jar

 

아파치나 NginX 같은 웹서버 없이 Spring Boot으로 만든 웹 어플리케이션에 무료 SSL 중 하나인 Let's Encrypt을 적용하게 되었다.

90일동안 사용가능하며 다시 갱신을 시켜줘야하나? 싶었지만 간단한 설정으로 자동갱신까지 가능하니 참 괜찮은 것 같다. 

그리고 Let's Encrypt SSL 인증서 발급 방법은 여러가지가 있는데 그 중 standalone 방식을 택했다. 

1) standalone
. 사이트 작동을 멈추고 이 사이트의 네크워킹을 이용해 사이트 유효성을 확인해 Let’s Encrypt SSL 인증서를 발급하는 방식
. 80포트로 가상 standalone 웹서버를 띄워 인증서를 발급
. 이 방식은 동시에 여러 도메인을 발급 받을 수 있음
. 자동갱신 가능
2) webroot
. 사이트 디렉토리 내에 인증서 유효성을 확인할 수 있는 파일을 업로드하여 인증서를 발급 
. 실제 작동하고 있는 웹서버의 특정 데렉토리의 특정 파일 쓰기 작업을 통해서 인증
. 이 방식의 장점은 서버를 중단없이 발급 가능
. 이 방법의 단점은 인증 명령에 하나의 도메인 인증서만 발급 가능
. 자동갱신 가능 
3) DNS 
. 도메인을 쿼리해 확인되는 TXT 레코드로 사이트 유효성을 확인하는 방법
. 와일드 카드 방식으로 인증서를 발급 가능
. 이 방법은 당연하게도 서버 관리자가 도메인 DNS를 관리/수정할 수 있어야 하며
. 인증서 갱신 시마다 DNS에서 TXT값을 변경해야

 

이제부터 standalone 방식으로 인증서를 발급받아 SSL을 적용해보자.

(적용하기 위해 이미 웹 어플리케이션은 배포된 상태여야 하며, 도메인 주소를 가지고 있어야 한다. ) 

STEP1. 터미널로 리눅스 서버에 원격 접속한다.

 

STEP2. Certbot을 설치한다. 

sudo apt-get install certbot

 

STEP3. 기존에 사용중인 80 포트는 kill 한다. 

 

STEP4. Certbot을 어떤 방식으로 구동시킬 지 선택한다. (난 standalone 방식을 택했다.) 

sudo certbot certainly --standalone

위 그림처럼 입력하면 이메일을 입력하라고 나타나고 이메일을 입력하면 된다.

 

곧이어 동의 절차글이 나타난다. A, Y를 차례대로 입력하고 마지막으로 ssl 인증설르 발급받을 도메인을 입력한다. 

여려기 입력 가능하다. ex) naver.com   www.naver.com  

위처럼 Congratulations!! 문구가 뜨면 성동! 그리고 /etc/letsencrypt/live/도메인/ 경로에 fullcahin.pem과 privkey.pem 가 발급되었다고 안내한다. 위에서 알려주는 경로로 이동하자.  (live 폴더 이상 안들어가진다면 접근권한이 없는 것이므로 sudo -i로 접근하거나 root로 들어가면 live폴더 이상 접근 가능하다.) 

 

STEP5. pem은 스프링 부트에서 인식을 못하므로, pem을 PKCS12 형식으로 변경

-fullchain.pem, privkey.pem 등이 발급받아진 경로로 이동 한 뒤 아래 명령어를 입력하면 keystore.p12 파일이 생성된다.

sudo openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out keystore.p12 -name ttp -CAfile chain.pem -caname root

- 위 명령어 진행 후 Enter Export Password가 나오는데 이때 비밀번호는 스프링 부트에 적용시 필요한 비밀번호이므로 잘 기억해두자.

 

STEP6. 스프링 부트에 적용시키기 위해 변환된 keystore.p12을 로컬로 가져와야한다.

난 FileZilla를 통해 keystore.p12이 있는 경로로 가서 로컬로 가져오는 방식을 택했다. (드래그 드랍만 해주면 로컬로 가져온다) 

 

STEP7. Spring Boot에 적용시키자

- resources 폴더 아래에 ssl 디렉토리를 생성한 뒤, 그곳에다가 keystore.p12을 넣자.

 

STEP8. Spring Boot의 application.yml 수정

server:
	port: 443
	ssl:
    	key-store: classpath:ssl/keystore.p12
    	key-store-type: PKCS12
    	key-store-password: 변환후입력헀던비밀번호

 

이제 웹 어플리케이션을 배포해서 https 까지 붙였을때 잘 뜨면 성공이다! 

 

 

인증서 자동갱신 하기

# Crontab 보기
$ crontab -l

# Crontab 편집
$ crontab -e

crontab -e 로 편집 모드로 들어갑니다.

그 다음 매달 1일에 자동 갱신 될 수 있는 아래 명령어를 입력하고 저장 후 나오면 끝!

$ 0 2 1 * * /usr/bin/certbot renew

인증서 만료일 확인하기

Certbot으로 부터 발급받은 인증서들에 대한 정보를 표시합니다.

$ certbot certificates

 

인증서 갱신 모의 테스트

인증서 renew 갱신이 제대로 동작하는지 테스트 해볼 수 있습니다.

certbot renew --dry-run

 

만약 certbot renew 도중 아래와 같은 에러가 발생한다면?

Attempting to renew cert (www.도메인.kr) from /etc/letsencrypt/renewal/www.도메인.kr.conf produced an unexpected error:
Problem bindging to port 80: Could not bind to IPv4 or IPv6......

 

apache2를 중단 하고 다시 실행해보자.

systemctl stop apache2.service

그리고 다시 certbot renew 로 실행하면 Congratulations, all renewals succeeded ..... 등의 내용이 뜸!!!

 

 

https://news.v.daum.net/v/20211211181030002?x_trkm=t 

 

"컴퓨터 사상 최악의 취약점 발견됐다".. 전세계 보안업계 '발칵'

거의 모든 인터넷 서버에서 광범위하게 사용되는 소프트웨어에 치명적 보안 취약점이 발견돼 전 세계 사이버 보안 업계가 발칵 뒤집혔다. 게임이나 클라우드 서버를 운영하는 정보기술(IT) 기업

news.v.daum.net

 

이번에 뉴스가 떴다. 바로 log4j 취약성 발견된 것. 우리회사에서도 대부분 사용하고 있어서 바로 작업에 들어가야했다

우선 취약점이 있는지 아래로 확인해보자

위 api실행결과 취약성이 있을 경우는 아래처럼 JNDI 연결을 시도한다. 

취약성이 없을 경우 JNDI 연결 정보가 그냥 출력된다.

취약성이 있다고 나오건 없다고 나오던간에 log4j 사용하는 플젝이라면 일단 조치를 취해보자..

Maven 사용 시:

1. 내 플젝에서 사용하는 log4j 버전을 아래 명령어로 확인해보자 (나는 2.14 나왔음..) 버전을 2.15.0으로 올려야한다.

./mvnw dependency:list | grep log4j

2. pom.xml에 아래를 추가

<properties>
    <log4j2.version>2.15.0</log4j2.version>
</properties>

3. 다시 한 번 아래 명령어로 확인해보니 2.15.0로 올려진 것을 확인!!

./mvnw dependency:list | grep log4j

Gradle 사용 시:

1. build.gradle 에서 log4j 버전을 아래처럼 명시한다. 

ext['log4j2.version'] = '2.15.0'

그리고 버전이 2.15.0으로 잘 업데이트 되었는지 확인하자 

 

참고할 log4j2 취약성과 스픙링 부트 사이트: 

https://spring.io/blog/2021/12/10/log4j2-vulnerability-and-spring-boot

Mybatis에서 파라미터를 DTO 혹은 Map 형식으로 받는 방법과 Map<String, Object> 형식으로 받는 방법을 알아보자.

 

1. 파라미터를 DTO 형식으로 받기

// DTO 클래스
@Data
public class MetaMngRequestDTO {

    private Map<String, Object> data;
    private String id;
}
// Service
public void updateMetaData(MetaMngRequestDTO dto) {
        testDAO.updateTest(dto);
}
// DAO Interface
 void updateTest(MetaMngRequestDTO dto);
// Mapper XML 
<update id="updateTest" parameterType="com.test.MetaMngRequestDTO">
        update TEST_TABLE
        set
        ITM_SEQ = #{data.ITM_SEQ},
        TEXT = #{data.TEXT}
        where SID = #{id}
</update>

쿼리쪽에 parameterType="넘겨주는 DTO 경로 및 이름"를 추가해야하며, DTO의 필드 중 String 타입 같은 경우는 #{id}로 곧장 쓸 수 있지만 Map형식일 경우는 #{필드명.key이름}으로 써준다 (ex. #{data.TEXT} ) 

(parameterType을 생략할 수 있으며, 그럴 경우 바로 dto의 필드명을 쿼리에 #{id} 이런식으로 곧장 사용 가능. 

2. 파라미터를 Map형식으로 받기 

// Service 클래스
public void insertTest(Map<String, Object> columns) {
        testDAO.insertItemYnSync(columns);
}
// DAO Interface
void insertTest(Map<String, Object> dataValues);
// Mapper XML
<insert id="insertTest" parameterType="hashmap">
        insert into LT_REG_META_ITM_YN
        (ITM_SEQ, WRITER, CONTENTS)
        values
        (#{ITM_SEQ}, #{WRITER}, #{CONTENTS})
</insert>

 

이번에는 parameterType=“hashmap”을 추가!

그리고 #{맵의 키}를 써주면 해당 키에 맞는 데이터가 들어간다.

예를 들면 

map에서 (“WRITER”, ”홍길동”) 이렇게 key-value가 있다고 치자. 그리곤 쿼리 쪽에서 #{WRITER} 작성하면, 쿼리는 실제홍길동으로 들어가서 실행된다. 

1. application.yml에 다음과 같이 추가

spring: 
   web:
    resources:
      chain:
        enabled: true
        strategy:
          content:
            enabled: true
            paths: /**

 

2. 타임리프 태그 th:href 와 th:src 사용

<link th:href="@{/main.css}" rel="stylesheet" type="text/css"/>
<script th:src="@{/js/main.js}" type="text/javascript"></script>

 

끝!

 

잘 적용 됐다면 개발자 도구를 열어 Elements 확인해보면 난수가 붙어서 나오는 것을 확인할 수 있다. 이러면 성공한것임

<link href="/main-0c362e5c8643b75ddf64940262b219f7.css" rel="stylesheet" type="text/css"/>
<script src="/js/main-c13acb86fa1012e27bbb01a7c4a9bf7f.js" type="text/javascript"></script>

 

 

 

 

참고: stackoverflow.com/questions/48732894/spring-mvc-and-thymeleaf-resource-versioning

1. 세션에 Data 저장

session.setAttribute("저장하고자 하는 변수명", 저장변수값);

<Controller>

@RequestMapping(value = "/test.do")
public String test(HttpServletRequest request) throws Exception {
        
    HttpSession session = request.getSession();
    String name = "홍길동";
    session.setAttribute("sessionId", name);
    
    return "test/test";
}

<View   ex.Thymeleaf>

<body>
    <h2 th:text="${sessionId}"></h2>   <!-- 출력값: 홍길동 -->
</body>

 

2. 세션에 저장된 Data 가져오기

session.getAttribute("저장한 변수명");

<Controller>

@RequestMapping(value = "/test2.do")
public String test2(HttpServletRequest request) throws Exception {
        
    HttpSession session = request.getSession();
    String name = (String) session.getAttribute("sessionId");
        
    System.out.println("==============================");
    System.out.println("세션에 저장 되 있는 변수 : "+name);  // 홍길동 출력
    System.out.println("==============================");
        
    name = "유재석";
    session.setAttribute("sessionId", name);

    return "test/test";
}

<View  ex.Thymeleaf>

<body>
    <h2 th:text="${sessionId}"></h2>   <!-- 출력값: 유재석 -->
</body>

 

 

3. 세션 초기화 하기

session.invalidate();

컨트롤러에서redirect를 할 때, 파라미터 값을 넘겨야 할때가 있는데

@RequestParam을 사용해주면 된다.

리다이렉트를 하는 부분 addAttribute해준다.

 

 

@RequestMapping("/test.do")
	public String insertComment(RedirectAttributes redirectAttributes) {
		
        int boardno = 2;
		redirectAttributes.addAttribute("boardno", boardno);
        
		return "redirect:selectPage.do";
	}

리다이렉트 받는 부분 RequertParam으로 맵핑함

@RequestMapping("/selectPage.do")
	public String selectPage(@RequestParam("boardno") int boardno){
		
		
		BoardVO boardPage = boardDAO.selectPage(boardno);
		
		model.addAttribute("boardPage", boardPage);
		
		return "boardView";
	}

스프링 프로젝트 진행 중, 데이터를 엑셀 형태로 다운로드 받을 수 있어야하는 상황이 생겼다.

이 포스팅에 올리는 소스코는 단순히 엑셀 파일로 다운 받게 해주는 간단한 템플릿(?)이고, 각자 원하는 형태에 맞춰 customize 하길 바람 ..! 

우선 엑셀 파일로 업로드 혹은 다운로드 기능을 사용하기 위해 아래와 같은 poi 관련 라이브러리가 필요하다..

<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>4.1.2</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>4.1.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml-schemas</artifactId>
			<version>4.1.2</version>
		</dependency>

 

 

아래는 소스다.

 

* 참고: HSSFWorkBook은 xls확장명으로 다운로드 받게하고, HSSWorkbook을 XSSFWorkbook로 변경만 해주면 xlsx확장명으로 다운로드 받게 해준다!! 

Controller

	@PostMapping("/excel/download")
    public void excelDownload(@RequestParam String fileName, HttpServletResponse response, Model model) throws Exception {

        HSSFWorkbook objWorkBook = new HSSFWorkbook();
        HSSFSheet objSheet = null;
        HSSFRow objRow = null;
        HSSFCell objCell = null;       //셀 생성

        //제목 폰트
        HSSFFont font = objWorkBook.createFont();
        font.setFontHeightInPoints((short)9);
       // font.setBoldweight((short)font.BOLDWEIGHT_BOLD);
        font.setFontName("맑은고딕");

        //제목 스타일에 폰트 적용, 정렬
        HSSFCellStyle styleHd = objWorkBook.createCellStyle();    //제목 스타일
        styleHd.setFont(font);
//        styleHd.setAlignment(HSSFCellStyle.ALIGN_CENTER);
//        styleHd.setVerticalAlignment (HSSFCellStyle.VERTICAL_CENTER);

        objSheet = objWorkBook.createSheet("첫번째 시트");     //워크시트 생성

        // 1행
        objRow = objSheet.createRow(0);
        objRow.setHeight ((short) 0x150);

        objCell = objRow.createCell(0);
        objCell.setCellValue("번호");
        objCell.setCellStyle(styleHd);

        objCell = objRow.createCell(1);
        objCell.setCellValue("이름");
        objCell.setCellStyle(styleHd);

        // 2행
        objRow = objSheet.createRow(1);
        objRow.setHeight ((short) 0x150);

        objCell = objRow.createCell(0);
        objCell.setCellValue("1");
        objCell.setCellStyle(styleHd);

        objCell = objRow.createCell(1);
        objCell.setCellValue("홍길동");
        objCell.setCellStyle(styleHd);


        response.setContentType("Application/Msexcel");
        response.setHeader("Content-Disposition", "ATTachment; Filename="+URLEncoder.encode("테스트","UTF-8")+".xls");

        OutputStream fileOut  = response.getOutputStream();
        objWorkBook.write(fileOut);
        fileOut.close();

        response.getOutputStream().flush();
        response.getOutputStream().close();
   }

 

View

 <form id="excelForm" name="excelForm" method="post" action="/excel/download">
        <input type="text" name="fileName" />
        <input type="submit" value="xlsx파일로 받기" />
 </form>

 

==============아래는 실행화면 확인 ================

원하는 파일명을 칸에 입력해주고 버튼을 누르면 엑셀파일이 바로 다운되면서 엑셀을 열면 다음처럼 나온다.

+ Recent posts