숫자형의 길이 구하기.

|

String a = "abced";
System.out.println("길이>>"+a.length);
  • 위와 같은 코드에서는 아래의 결과가 나올것이다.

길이: 5

  • String 형의 경우 length 함수를 지원하지만, int형은 길이 함수가 없다. 이럴경우 Math 함수를 사용하면 자리수를 구할 수 있다.
int a = 3291;
int length = (int)(Math.log10(a)+1);
System.out.println("길이 : " + a);

길이 : 4


Spring boot 환경에서 Amazon s3에 이미지파일 업로드하기.

|

amazon s3 인증 받기 및 설정 관련 내용은 생략하였다.

프로젝트 구성을 위해 의존성을 추가.

  • pom.xml
....
<dependency>
	<groupId>com.amazonaws</groupId>
	<artifactId>aws-java-sdk</artifactId>
	<version>1.11.313</version>
</dependency>
....
  • application.yml 설정파일.
spring
 aws:
  access-key: 발급받은 accessKey
  secret-key: 발급받은 secretKey
  region: 지역
  bucket: s3 버킷이름
  url: S3 url
  • AWS properties Configuration 추가.
@Configuration
@ConfigurationProperties(prefix="spring.aws")
public static class AwsProperties{
	private String accessKey;
	private String secretKey;
	private String region;
	private String bucket;
	private String url;

	//<editor-fold desc="getter setter">
	public void setAccessKey(String accessKey){
		this.accessKey=accessKey;
	}

	public void setSecretKey(String secretKey){
		this.secretKey=secretKey;
	}

	public void setRegion(String region){
		this.region=region;
	}

	public String getBucket(){
		return bucket;
	}

	public void setBucket(String bucket){
		this.bucket=bucket;
	}

	public String getUrl(){
		return url;
	}

	public void setUrl(String url){
		this.url=url;
	}
}

  • AmazonS3Client 빈 등록
      
    import com.amazonaws.auth.AWSStaticCredentialsProvider;
    import com.amazonaws.auth.BasicAWSCredentials;
    import com.amazonaws.regions.Regions;
    import com.amazonaws.services.s3.AmazonS3;
    import com.amazonaws.services.s3.AmazonS3ClientBuilder;
    .....  
    @Bean
    public AmazonS3 amazonS3Client(AwsProperties awsProperties){
    	return AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsProperties.accessKey, awsProperties.secretKey))).withRegion(Regions.fromName(awsProperties.region)).build();
    }
    ...
    
  • Controller에서 이미지 업로드 호출 부분.

@Controller
public class UploadController {

	@Autowired 
	private UploadService uploadService
	....
	@PostMapping("/uploadImage")
	public void background(
			@RequestParam("userImage") MultipartFile multipartFile
	){
		String uploadUrl = "업로드 url 정보";
		uploadService.updateBackgroundImage(uploadUrl, multipartFile);
	}
	....
	
}
  • Service 에서 AmazonS3 에 이미지 업로드 하는 부분.

@Service
public class UploadService {

	@Autowired
	private AmazonS3 amazonS3;
	
	@Autowired
	private AwsProperties awsProperties;
	
	....
	// controller 에서 실행한 부분.
	public void updateBackgroundImage(String url, MultipartFile multipartFile){
        upload(url, multipartFile);
    }
	
	private void upload(String url, MultipartFile file){
        try{
            String uploadFileName = file.getOriginalFilename();
            String fileExtension = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1, uploadFileName.length());

            String contentTypeTail = "jpeg";
            if (fileExtension.toLowerCase().equals("gif")) contentTypeTail = "gif";
            else if (fileExtension.toLowerCase().equals("png")) contentTypeTail = "png";

            byte[] bytes = file.getBytes();
            upload(url, new ByteArrayInputStream(bytes), contentTypeTail);
        }catch(Throwable t){
            throw new JmlimException(t.getMessage(), HttpStatus.BAD_REQUEST, t);
        }
    }
	
	private void upload(String key, InputStream inputStream, String imageType){
        ObjectMetadata metadata = new ObjectMetadata();
        if(imageType != null) {
            metadata.addUserMetadata("Content-Type", "image/"+imageType);
        }

        PutObjectRequest putObjectRequest = new PutObjectRequest(awsProperties.getBucket(), key, inputStream, metadata);
		// 퍼블릭으로 공개하여 올림.
        putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);
        amazonS3.putObject(putObjectRequest);
    }
}

Java AES256 Sample 코드.

|

AES 란?

Advanced Encryption Standard를 줄인 말이다. 한국어로 번역하면 ‘고급 암호화 표준’이다. 대칭키를 쓰는 블럭 암호이다. 높은 안전성과 속도로 인해 인기를 얻어 전 세계적으로 많이 사용되고 있다.

특징

대칭형, 블럭 암호화 알고리즘이다.
대칭형 암호화 알고리즘 중 가장 유명하다.
암호화 키는 128, 192, 256의 세 가지 중 하나가 될 수 있으며, 각각 AES-128, AES-192, AES-256으로 불린다.

AES256Cipher.java

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidAlgorithmParameterException;

import org.apache.commons.codec.binary.Base64;

public class AES256Cipher {

    private static volatile AES256Cipher INSTANCE;

    final static String secretKey = "jmlim12345bbbbbaaaaa123456789066"; //32bit
    static String IV = ""; //16bit

    public static AES256Cipher getInstance() {
        if (INSTANCE == null) {
            synchronized (AES256Cipher.class) {
                if (INSTANCE == null)
                    INSTANCE = new AES256Cipher();
            }
        }
        return INSTANCE;
    }

    private AES256Cipher() {
        IV = secretKey.substring(0, 16);
    }

    //암호화
    public static String AES_Encode(String str) throws java.io.UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        byte[] keyData = secretKey.getBytes();

        SecretKey secureKey = new SecretKeySpec(keyData, "AES");

        Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
        c.init(Cipher.ENCRYPT_MODE, secureKey, new IvParameterSpec(IV.getBytes()));

        byte[] encrypted = c.doFinal(str.getBytes("UTF-8"));
        String enStr = new String(Base64.encodeBase64(encrypted));

        return enStr;
    }

    //복호화
    public static String AES_Decode(String str) throws java.io.UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        byte[] keyData = secretKey.getBytes();
        SecretKey secureKey = new SecretKeySpec(keyData, "AES");
        Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
        c.init(Cipher.DECRYPT_MODE, secureKey, new IvParameterSpec(IV.getBytes("UTF-8")));

        byte[] byteStr = Base64.decodeBase64(str.getBytes());

        return new String(c.doFinal(byteStr), "UTF-8");
    }
}

AES256CipherTest.java


import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Test;

public class AES256CipherTest {
    String id = "hackerljm";
    String custrnmNo = "1234";
    String custNm = "정묵테스트";

    @Test
    public void encDesTest() throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        AES256Cipher a256 = AES256Cipher.getInstance();

        String enId = a256.AES_Encode(id);
        String enYyyymmdd = a256.AES_Encode(custrnmNo);
        String enCustNm = a256.AES_Encode(custNm);

        String desId = a256.AES_Decode(enId);
        String desYyyymmdd = a256.AES_Decode(enYyyymmdd);
        String desCustNm = a256.AES_Decode(enCustNm);

        assertThat(id, is(desId));
        assertThat(custrnmNo, is(desYyyymmdd));
        assertThat(custNm, is(desCustNm));
    }
}

참고:

  • https://dukeom.wordpress.com/2013/01/08/aes256-%EC%95%94%ED%98%B8%ED%99%94-java-%EC%83%98%ED%94%8C/
  • https://namu.wiki/w/AES

Spring boot 환경에서 Spring Session을 통해 WAS가 아닌 다른 저장소에 세션 저장하기.

|

WAS의 세션을 이용했을 때, 서버가 한 대일 때에는 WAS의 세션을 사용하는데에 문제가 없었을 것이다. 사용자가 증가함에 따라 서버에 과부하가 걸려 WAS를 증설하기 시작할 경우 HttpSession을 사용하면 세션이 두군데서 관리된다.

ex) 로그인 요청은 1번 서버로 들어와 1번 서버의 세션에 담았는데, 그 다음 요청을 2번에 하게 된다면?? 로그인 상태가 아니다.

그렇기 때문에 단일 서버 환경에서 세션 기능을 사용하기 위해서는 HttpSession 객체를 활용해도 상관없지만, 멀티 서버 환경에서 세션 기능을 사용하기 위해서는 세션 정보를 Redis와 같은 DB에 저장해야 한다.

리눅스에 Redis 설치하기.

먼저 리눅스에 redis를 설치하기 위해서 파일을 직접 다운받거나 apt-get 등을 이용할 수 있다. 만약 apt-get 패키지 다운로드를 사용할 경우 아래와 같이 커맨드를 입력하면 된다.

$ sudo apt-get install redis-server

redis 동작하기 –

$ redis-server

Spring Boot에서 Spring session + redis 로 세션 관리 하기.

의존성 추가.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session</artifactId>
</dependency>

application.yml 추가.

spring: 
  redis:
    host: 192.168.0.156 # 레디스가 설치되어있는 서버의 아이피
    port: 6379

Spring Session Configuration 추가.

import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;

@Configuration
@EnableRedisHttpSession
public class RedisSessionConfig extends AbstractHttpSessionApplicationInitializer {
} 

레디스에서 세션 저장여부 확인.

참고:

  • http://wiki.sys4u.co.kr/pages/viewpage.action?pageId=8552454
  • https://webisfree.com/2018-01-25/%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90-redis-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-install

스프링부트에서 다국어 기능 사용하기.

|

기존 Spring + java web application 다국어 지원 설정을 하기 위해선 여러가지 사항들을 고려 해야 한다. spring framework 및 설정을 통해서 다국어를 사용하는 방법에 여러가지가 있다.

하지만 Spring Boot의 가장 큰 장점은 대부분의 설정이 이미 되어있어서 별로로 설정하지 않아도 사용 가능하다. MessageSource 설정도 역시 이미 되어있다.

추기로 이 블로그에선 yml 파일을 사용하여 다국어를 설정해 보는 방법을 작성하였다.

다국어 관련 Config 파일 작성.

yml YamlResourceBundle 클래스를 사용하기 위한 pom.xml 파일 디펜던시 추가.

<!-- https://mvnrepository.com/artifact/net.rakugakibox.util/yaml-resource-bundle -->
<dependency>
  <groupId>net.rakugakibox.util</groupId>
  <artifactId>yaml-resource-bundle</artifactId>
  <version>1.1</version>
</dependency>

Configuration 작성.

package 파일이 들어갈 패키지명;

import net.rakugakibox.util.YamlResourceBundle;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

@Configuration
public class MessageSourceConfig extends WebMvcConfigurerAdapter {

     @Bean
    public LocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
        slr.setDefaultLocale(Locale.KOREA);
        return slr;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        return lci;
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
    
    @Bean("messageSource")
    public MessageSource messageSource(
            @Value("${spring.messages.basename}") String basename,
            @Value("${spring.messages.encoding}") String encoding
    ) {
        YamlMessageSource ms = new YamlMessageSource();
        ms.setBasename(basename);
        ms.setDefaultEncoding(encoding);
        ms.setAlwaysUseMessageFormat(true);
        ms.setUseCodeAsDefaultMessage(true);
        ms.setFallbackToSystemLocale(true);
        return ms;
    }
}

class YamlMessageSource extends ResourceBundleMessageSource {
    @Override
    protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
        return ResourceBundle.getBundle(basename, locale, YamlResourceBundle.Control.INSTANCE);
    }
}

src/main/resources/appilcation.yml 에 다국어 설정파일이 있는 경로 추가.

spring: 
 .....
  messages:
    basename: i18n/messages
    encoding: UTF-8

src/main/resources/i18n/messages.yml 파일

alert:
 confirm: "확인"
 cancel: "취소"
 loginEmail: "아이디(이메일)를 입력해 주세요."
 loginPassword: "비밀번호를 입력해 주세요."
 loginFail: "아아디(이메일) 또는 비밀번호가 올바르지 않습니다." 

src/main/resources/i18n/messages_en.yml 파일

alert:
 confirm: "Ok"
 cancel: "Cancel"
 loginEmail: "Please enter your ID(Email)."
 loginPassword: "Please enter a password."
 loginFail: "The email or password is invalid." 

src/main/resources/i18n/messages_jp.yml 파일

alert:
 confirm: "確認"
 cancel: "取り消し"
 loginEmail: "ID(Eメール)を入力してください"
 loginPassword: "パスワードを入力してください"
 loginFail: "ID(Eメール)またはパスワードをもう一度ご確認ください。" 

src/main/resources/i18n/messages_zh.yml 파일.

alert:
 confirm: "确认"
 cancel: "取消"
 loginEmail: "请输入用户名(邮箱)。"
 loginPassword: "请输入密码。"
 loginFail: "请重新确认用户名(邮箱)或者密码。" 

jsp 페이지에서 사용하기

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <-- 이와같이 태그 선언  

<spring:message code="alert.loginEmail" /> 원하는 텍스트가 쓰여진 메세지 코드값 정의

thymeleaf 페이지에서 사용하기.


  ...
  <div>
    <span th:text="#{alert.loginEmail}">아이디(이메일)를 입력해 주세요.</span>
  </div>
  ...

default url 조회

영문조회 시 (lang=en)

일문 조회시 (lang=jp)