Author Archives: opendocs

[Source | Java] InputStream to String

Java에서 InputStream객체를 String으로 변환하는 코드를 정리한다.


작성일 : 2021-04-23


// Define
InputStream is = {SomeData};

1> BufferedReader 객체사용

InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
StringBuffer sb = new StringBuffer();
String line;
while((line = br.readLine()) != null) {
	sb.append(line);
	sb.append('\r');
}
br.close();
// Result
String str = sb.toString();

2> StringBuffer 객체사용

StringBuffer sb = new StringBuffer();
byte[] b = new byte[4096];
int length;
while((length = is.read(b)) != -1) {
	sb.append(new String(b, 0, length));
}
// Result
String str = sb.toString();

3>  ByteArrayOutputStream 객체사용

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[4096];
int length;
while((length = is.read(b)) != -1) {
	baos.write(b, 0, length);
}
baos.flush();
// Result
String str = new String(baos.toByteArray());

[Source | Nodejs] 이메일 발송

Nodejs에서 이메일 발송코드를 정리한다.


작성일 : 2021-04-22


1> nodemailer 설치

npm install nodemailer

2> 코드작성

let transporter = nodemailer.createTransport({
   host: "OPENDOCS_SMTP_HOST",   // SMTP Host
   port: 465,
   secure: true,                 // true for 465, false for other ports
   auth: {
      user: "OPENDOCS_USERID",   // User ID
      pass: "OPENDOCS_USERPW"    // User Password
   }
});

let info = await transporter.sendMail({
   from: '"OPENDOCS" <corp@opendocs.co.kr>',     // Sender Address
   to: "opendocs@naver.com, opendocs@kakao.com", // Receiver List
   subject: "Hello ✔", // Subject line
   text: "Hello world?", // plain text body
   html: "<b>Hello world?</b>" // html body
});

[Setting | AWS] Simple Email Service 설정

AWS에서 제공하는 Simple Email Service를 설정하고 시스템에서 메일이 발송이 필요할 경우 활용하도록 하자.


작성일 : 2019-08-07

1> 설정메뉴로 이동

  • 서비스 > 고객 참여 > Simple Email Service 선택
  • 미국 서부 (오레곤) 선택 – (원하는 지역으로 선택)
  • Identity Management > Email Address 선택

2> 발송시 사용될 이메일 인증

  • Verify a New Email Address 선택
  • 이메일 입력
  • 입력된 메일로 인증 URL이 발송됨
  • 브라우저에서 인증 URL로 이동하면 축하메시지와 함께 인증이 완료됨

3> Verification Status 의 값이 verified 로 바뀌었음을 확인


4> 다량의 메일 발송을 위한 설정

  • 최초 인증시 메일발송량이 200개로 제한 됨
  • Email Sending > Sending Statistics 선택 > Request a Sending Limit Increase 선택
  • Create case > Service limit increase 선택 > 아래사항입력 > Submit 선택
  • 아래 설정값 입력
Case classification
>> Limit Type : SES Sending Limits
>> Mail Type(optional) : System Notification
>> 나머지는 기본값
Requests
>> Region : US West (Oregon)
>> Limit : Desired Daily Sending Quota
>> New Limit value : 10000
Case description
>> Use case description : To sign up and send announcements

5> AWS 승인을 기다림

  • 접수완료 후 24시간 정도 후 승인메일을 받게됨

6> SMTP 발송 설정

  • 설정을 통해 SMTP 발송계정 발급
  • Email Sending > SMTP Settings > Create My SMTP Credentials 선택
  • IAM User Name 입력 > 생성 버튼 클릭
  • 자격 증명 다운로드 클릭 > credentials.csv 파일 다운로드
  • 생성된 계정 & 서버설정 정보를 통해 이메일 발송

[Source | C++] 키워드 목록

C++키워드의 경우 변수명으로 사용이 불가능하다. 키워드 목록을 정리해보자.


작성일 : 2021-04-20


asmautobool
breakcasecatch
 charclassconst
const_cast continue default
 deletedodouble
dynamic_castelseenum
 explicitexportextern
falsefloatfor
 friendgotoif
inlinentlong
 mutablenamespacenew
operatorprivateprotected
 publicregisterreinterpret_cast
returnshortsigned
sizeofstaticstatic_cast
structswitchtemplate
thisthrowtrue
trytypedeftypeid
typenameunionunsigned
usingvirtualvoid
volatilewchar_twhile

[Concept | Encription] 암호화 유형

암호화의 종류와 장단점을 정리하고 시스템 개발에 어떤 유형이 적용되는지 알아보자.


작성일 : 2017-03-04
수정일 : 2021-04-15

1> 암호화의 종류

유 형 특 징 종 류
단방향 해쉬함수 암호화키 없음 SHA, HAS-160, MD5
양방향 대칭키 블록암호 암호화키 == 복호화키 DES, AES, SEED, HIGHT
스트림암호 A5/1, A5/2, A5/3
비대칭키 암호화키 != 복호화키 RSA, EIGamal, 전자서명

2> 단방향 암호화 – 해쉬함수

① Encryption : 송신자는 해쉬함수를 이용해 평문을 암호화 한다.
② Transmit : 암호문을 수신자에게 전송한다.
③ Compare : 전송된 암호문과 저장된 암호문을 비교한다.
※ 암호문을 복호화 하는것은 불가능하다.

장점
* 암호화키를 사용하지 않으므로 공유절차 없이 보안을 유지할 수 있음
단점
* 복호화가 필요한 경우에는 사용이 불가능함
사용예
  사용자 개인정보를 암호화 하여 디비에 저장하고 인증시 암호화된 값으로만 비교

3> 양방향 암호화

3-1> 대칭키 암호화 (비밀키 암호화)

암호화 키를 생성하여 송신자(A)와 수신자(B)가 공유하는 방식.
① Create Private Key And Share : 암호화키를 생성하여 서로 공유한다.
② Encryption : 공유된 암호화키를 이용해 평문을 암호화 한다.
③ Transmit : 암호문을 수신자에게 전송한다.
④ Decryption : 공유된 암호화키를 이용해 암호문을 복호화 한다.

장점
* 암호화 연산 속도가 빨라 효율적으로 시스템 구축가능
단점
* 암호화 / 복호화를 위해 키가 공유되어야 하므로 노출되기 쉬움
* 키가 노출될 우려 때문에 개발 버전업 또는 일정 주기로 변경관리 해주어야 함
  (노출될 우려가 전혀 없다면 최초 생성된 키를 계속 사용하기도 함)

3-1-1> 블록 암호

고정된 크기의 블롣단위로 암·복호화 연산을 수행하며 각 블록의 연산에는 동일한 키가 이용된다. 전치와 환자를 반복시켜 암호화하면 평문의 통계적 성질이나 암호 키와의 관계가 나타나지 않아 안전한 암호를 구성할 수 있다.

3-1-2> 스트림 암호

이진화된 평문 스트림과 이진 키스트림 수열의 XOR 연산으로 암호문을 생성하는 방식으로 주기, 선형 복잡도 등 안전성과 관련된 수학적 분석이 가능하고 알고리즘 구현이 쉬운 특징이 있다.


3-2> 비대칭키 암호화 (공개키 암호화)

송신자(A)는 수신자(B)의 공개키(Public Key)로 암호화하여 전송하고 수신자(B)는 비밀키(Secret Key)로 복호화함.
① Create Key Pair And Share Public Key : 키쌍을 생성하고 공개키를 공유한다.
② Encryption : 공유된 공개키로 평문을 암호화 한다.
③ Transmit : 암호문을 수신자에게 전송한다.
④ Decryption : 자신만 알고있는 비밀키를 이용해 암호문을 복호화 한다.

장점
* 다른 유저와 키를 공유하지 않더라도 암호화가 가능함.
* 공개키로 누구나 암호화를 할 수는 있지만 송신자만 보유한 비밀키가 없으면 복원이 불가함.
* 연산이 많고 복잡하나 공개키와 비밀키를 활용한 광범위한 인증이 제공됨.
단점
* 수학적인 난제를 기반으로 설계되어 연산비용이 높아 효율성이 떨어짐.

4> 자세한 설명은 [KISA 홈페이지] 참조

[Concept | Encription] 암호화의 기본개념

시스템 개발시 악의적인 공격을 피하기 위해 암호화는 필수적으로 적용되어야 한다. 암호화의 기본적인 의미와 원리를 정리해보자.


작성일 : 2017-03-01
수정일 : 2021-04-09

1> 사전적의미의 암호화

암호화(encryption)란? 정보가 노출되는 것을 방지하기위해 특정 알고리즘을 이용하여 변형하는 것.

  • 암호화를 역행하여 다시정보를 읽게 하는 것은 복호화(decryption)라고 함.
  • 군사와 정부의 은밀한 대화를 위하여 오랫동안 이용되어 왔음.
  • 근래 기업에서는 데이터 전달개인의 정보를 저장하는데 자주 이용함.

2> 시스템 개발에서의 암호화

보안이 필요한 정보를 특정 알고리즘을 통해 의미없는 문자열(이진수 덩어리)로 바꾸는 것.

  • 악의적인 목적으로 암호를 해독해 공격하는 것을 방지할 수 있음.
  • 보안에 있어 시스템적인 보호 방법 보다 근본적인 해결방법이라 할 수 있음.

3> 암호화의 기본조건

암호화는 아래 두가지 조건을 충족해야 함.

* Confusion(혼돈) : 원문의 내용을 짐작하기 어렵게 만들어야 한다.
* Diffusion(확산) : 알고리즘의 패턴을 추론하기 어렵게 만들어야 한다.
  • 가장 좋은 암호화는 알고리즘을 알아내기 어렵게 해서 정보를 해독하지 못하게 하는 것이지만 항상 암호화 하려는 자와 복호화 하려는자의 노력이 있을뿐 100% 해독 불가능한 암호화란 있을 수 없음.

4> 고전적인 암호화

아래 설명하는 치환(Substitution)과 이동(Transposition)을 교차적용해 암호화 하였음.

4-1> 치환 (Substitution) : 단순히 다른문자로 치환하여 혼돈의 조건을 만족시킴.

4-2> 이동 (Transposition) : 무작위로(실제로는 특정규칙이 존재) 다른문자로 치환하여 확산의 조건을 만족시킴.


5> 암호화의 발전

컴퓨터의 등장으로 과거 기계식에 비해 계산능력이 월등해 졌고 그앞에서 고전적인 암호화는 무용지물이 되었다. 따라서 안전성 보완을 위해 임의의 문자열값인 암호화키(Encryption Key)를 활용하게 되었음.

5-1> 원리

암호화키와 평문(Plain Text)을 바이너리 형태로 변형한 후 배타적 논리합(XOR) 연산에 따라 암호화 및 복호화를 함.

ABA XOR B
000
101
011
110
[참고] XOR 연산

5-2> 암호화 규격

암호화키의 길이나 사용횟수가 제약이 없다면 복잡도를 무한히 증가시켜 거의 완벽한 암호화가 가능하겠지만 복호화 하는데 비용(시간소모)이 계속 커진다면 사용할 수가 없을 것이기 때문에 일정한 규격에 의한 암호화를 고려해야 함.

5-3> 양자연산

양자연산이 가능한 컴퓨팅 방식으로 인해 연산속도가 월등히 빨라진다면 위험하지 않느냐는 의문이 제기 되고는 있지만 양자연산을 통한 해독 알고리즘이 발전되기 전까지는 안전하다고 함.

5-4> 암호화의 불완전성

현재까지 주로 사용되는 방식의 암호화는 엄밀히 말하여 완벽한 안전을 보장하는 것이 아니라 현실적으로 풀기 어렵다는 조건부 안전에 의존하고 있음.

[Source | MyBatis] DB 부하를 줄이기 위한 쿼리캐싱 기능

거의 바뀌지 않거나 고정된 값을 디비에서 자주 쿼리해야할 경우가 있다. 이때 마이바티스에서 제공하는 쿼리캐싱 기능 사용법을 알아보자.


작성일 : 2021-04-06

https://mybatis.org/mybatis-3/ko/sqlmap-xml.html#cache
공식문서에 잘 설명이 되어있다.


1> 캐싱적용시 고려해야하는 사항들

* flush / set 하는 시점을 명확히 정의 해야한다.
--> 단순히 부하를 줄인다고 여기저기 사용했다가 갑자기 이상한 데이터로 에러가 발생할 수 있고 추적 또한 어렵다.
* 마이바티스의 경우 쿼리 단위로 캐싱을 해줘 문제가 되지 않지만 Redis나 Memcached, ehcache등을 사용한다면 값저장시 키값을 잘 정의해야한다.
--> 보통 키값에 prefix(객체명+'_')를 주어 구분한다.
* 데이터가 자주 변경되는 경우 사용여부를 다시한번 고려해보자.
--> 변경시 flush하고 확인시 다시 set해줘야 하기 때문에 불필요한 절차만 추가할 뿐이다.

2> 적용예제

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.opendocs.www.repository.categorys.CategorysDaoMapper">
    
   <!-- Configuration Caching -->
   <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

   <!-- Insert Query Flush Cache --> 
   <insert id="insert" parameterType="categorys">
      INSERT INTO opendocs_www.categorys (uid, pid, name, status, created_at) 
         VALUES(#{uid}, #{pid}, #{name}, #{status}, now())
   </insert>

   <!-- Select Query Use Cache --> 
   <select id="select" parameterType="categorys" resultType="categorys">
      SELECT uid, pid, name, status, created_at, updated_at 
      FROM opendocs_www.categorys T1 
   </select>
	
   <!-- Update Query Flush Cache --> 
   <update id="update" parameterType="categorys">
      UPDATE opendocs_www.categorys
      <set>
         <if test="1 == 1">updated_at = now(),</if>
         <if test="pid != null">pid = #{pid},</if>
         <if test="name != null">name = #{name},</if>
         <if test="status != null">status = #{status},</if>
      </set> 
      WHERE uid = #{uid}
   </update>
	
   <!-- Delete Query Flush Cache --> 
   <delete id="delete" parameterType="categorys">
      DELETE FROM opendocs_www.categorys WHERE uid = #{uid}
   </delete>
	
</mapper>
위의 예제는 select시 캐싱하고 insert, update, delete시 캐싱된 데이터를 지운다.
<cache/>를 선언하게 되면 다음 속성을 선언한 것과 같으며 특정쿼리가 캐싱되지 않기를 원한다면 useCache="false"를 선언해주면 된다.
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

2-1> eviction : 캐싱전략

* LRU - 가장 오랜시간 사용하지 않는 객체를 제거
* FIFO - 캐시에 들어온 순서대로 객체를 제거
* SOFT - 가비지 컬렉터의 상태와 강하지 않은 참조(Soft References )의 규칙에 기초하여 객체를 제거
* WEAK - 가비지 컬렉터의 상태와 약한 참조(Weak References)의 규칙에 기초하여 점진적으로 객체 제거

2-2> flushInterval : 플러시 주기 (밀리세컨드 단위 1초 = 1000)

* 밀리세컨드 단위로 1초이면 1000
* 기본값없음 (미선언시 쿼리수행시만 동작)

2-3> size : 캐싱 사이즈

* 메모리 상황을 고려한 적정한 사이즈
* 기본값 1024

2-4> readOnly : 읽기전용 여부

* 모든 호출자에게 캐시된 객체의 같은 인스턴스(변경불가)를 리턴
* 사용시 성능 향상
* 기본값 false

3> 다른 캐싱방법

상황에 맞추어 아래 캐싱방법도 고려하여 보자.

  • Memcached
  • Redis
  • ehcache

[Setting | VSCode] ftp / sftp 접속

CI환경이 구축되지 않아서 수정한내용을 번거롭게 개발 & 배포를 해야할 경우가 있다.
이때 번거로움을 피하고자 서버에 직접 접속하여 수정하게 된다.
(물론, 이력관리서비스 장애에 취약하게 되니 추천되는 방법은 아니다.)
Visual Studio Code 에서 sftp로 서버에 직접 접속하여 소스를 수정하는 방법을 정리해 본다.


작성일 : 2021-03-27

1> Extenstions(단축키 Ctrl+Shift+X) > ftp-simple을 검색하여 설치한다.


2> 설치가 완료되면 단축키 Ctrl+Shift+P > ftp-simple : Config – FTP connection setting 실행


3> 다음과 같이 설정하고 저장

name : 접속구분
host : 서버호스트
port : 접속포트
type : ftp or sftp
username : 사용자명
password : 패스워드
path : 수정할 폴더경로
autosave : 수정후 저장시 자동으로 서버에 수정 및 저장여부
confirm : 저장시 덮어쓸지여부를 선택
privateKey : 접속시 키파일이 필요할경우 경로를 입력

※ 전체옵션

name - string - Display name.
host - string - server domain or ip.
port - number - (option) port (Default: : 21)
type - string - (option) ftp type. 'ftp' or 'sftp' (Default: : 'ftp'). If 'ftp' does not work, try 'ftp2'.
username - string - username for authentication.
password - string - (option) password for authentication.
privateKey - string - (option) (only sftp) String that contains a private key for either key-based or hostbased user authentication (OpenSSH format) Default: none
passphrase - string - (option) Use sftp 'privateKey' only. For an encrypted private key, this is the passphrase used to decrypt it. Default: none
agent - string - (option) (only sftp) Path to ssh-agent's UNIX socket for ssh-agent-based user authentication. Important: Windows users: set to 'pageant' for authenticating with Pageant or (actual) path to a cygwin "UNIX socket." Default: none
agentForward - boolean - (option) (only sftp) Set to true to use OpenSSH agent forwarding (auth-agent@openssh.com) for the life of the connection. agent must also be set to use this feature. Default: false
secure - boolean - (only ftp) Explicit FTPS over TLS, default: false
secureOptions - object - (only ftp) Options for TLS, same as for tls.connect() in Node.js.
path - string - (option) remote root path. Default: '/'
autosave - boolean - (option) To determine whether the automatically uploaded when you open a file directly and modify and save. Default: true
backup - string - (option) The local path you want to back up before file saving on the server.
confirm - boolean - (option) Only save option. When you save the file, ask if you want to overwrite the file if it already exists.. Default: true
project - object - (option) Only save option. Pre-specify local workspace path and server root path to save directly without selecting a path. Overwrite unconditionally.
ignore - array - (option) Only 'Remote directory open to workspace' option. Path to be ignore. Use glog pattern. (Caution : server path (ex:/home) + ignore pattern (ex:/**/node_modules) => /home/**/node_modules)

4> 단축키 Ctrl+Shift+P > ftp-simple : Remote directory open to workspace

선택하면 3>에서 설정한 접속구분명이 보여지고 접속할 폴더도 선택가능하다.


5> 파일 수정이 되지 않는다면…

파일에 권한을 확인해야 한다.
접속한 사용자가 파일수정권한이 없을경우 에러메시지 없이 서버에 내용이 반영되지 않는다.
파일의 소유자를 변경하거나 접속 username을 변경해야 한다.

[Guide | App – iOS] 앱개발시 테스트환경 구축

배포환경 구축의 중요성과 iOS환경에서 사내배포하는 방법에 대하여 정리해보자.


작성일 : 2019-08-17

앱개발이 완료되면 버그 및 개선사항을 이슈화하고 수정하기 위해 사내배포가 필요하다.

수정 -> 배포 -> 수정 -> 배포 -> 수정 -> 배포 -> 수정 -> 배포 ...

한번에 수정하고 완료된다면 이상적이겠지만 대부분의 경우 수정과 배포를 여러번 반복하게된다.

배포절차를 정리하고 자동화가 필요한 이유이다.


iOS의 경우 단순히 빌드파일(.ipa)로 사내배포가 불가하고 다음 4가지 방법으로 정리된다.

1> 디바이스를 직접 맥에 연결하여 빌드

여러기기에서 테스트를 해야하는 경우 번거롭다.


2> AdHoc 배포 (‘특별한 목적을 위해서’라는 뜻의 라틴어)

기기의 UDID를 알아내는 절차만 거치면 가장 깔끔한 방법이다.
[LINK - Device UDID 확인]

3> TestFlight 배포

테스트 기기를 위한 계정생성 및 검수 등의 불편하다.

4> Enterprise 배포

매우 편리하지만 1년에 300달러(?)가량의 비용이 발생한다.

결론을 내리자면 규모가 있고 비용부담이 없다면 4> Enterprise, 스타트업과 같이 인원이 적고 비용을 부담하고 싶지 않다면 2> AdHoc 배포가 현명한 방법이 아닐까 생각된다.

[Tip | Java – Spring] BeanCreationException

서비스 시작시 아래메시지가 발생하였다. 문제를 확인하고 해결해보자.
Error Message.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘OpdController‘: Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘OpdService‘ available


작성일 : 2021-03-24

컨텍스트에 없는 빈을 주입하려 할때 발생한다.
OpdService에 @Service 어노테이션을 선언해주어야 한다.

@Controller
public class OpdController {

   @Autowired
   private OpdService opdService;
   ...

}
@Service
public class OpdServiceImpl implements OpdService {
   ...
}

다른 빈 ID를 사용하려 했을때 실수로 잘못된 이름에 주의한다.

@Controller
public class OpdController {

   @Autowired
   @Qualifier("renameService")
   private OpdService opdService;
   ...

}
<!-- XML 선언 -->
<bean id="renameService" class="com.opendocs.project.OpdServiceImpl" />
// Annotation 선언
@Service("renameService")
public class OpdServiceImpl implements OpdService {
   ...
}