2019년 상반기 회고

일년이 어떻게 흘러간지 한해가 지날수록 더 기억이 안 나서
정리의 필요성을 느껴, 올해부터는 회고를 진행해볼까 합니다.

💼 회사


01월~02월

서버리스 아키텍쳐 구현 마무리


12월부터 설계 및 개발을 진행하였던 서버리스 아키텍처를 12월 중순쯤 배포하였으나,
이슈 사항들이 많아 마무리 짓는데 생각보다 오래 걸렸습니다.

외부 싱크 여부를 판단하기 위한 데이터들을 Insert 또는 Update 하다 보니,
RDS 콘솔 상에서 IOPS 쓰기가 1000 이상이 되는 경우가 발생했습니다.

해당 데이터를 레디스(Elastic Cache)나 noSQL(DynamoDB)에 쌓은 후, 다시 RDS로 옮기는 것도 검토해보았지만, 실제 구현할 경우 관리 포인트가 너무 많이 발생하게 되어 연동 속도를 줄이더라도 Lambda의 동시성을 조절하는 것으로 처리하였습니다.

02월~03월

자세히 보기

[ETC]시니어 개발자의 조건을 다시 읽고

시니어 개발자의 조건이라는 포스팅을 2017년 초에 접하고,

북마크에 담아두었다가 오늘에서야 다시 열어보게 되었습니다.

주요 키워드는 아래의 6가지였습니다.

  • 시스템을 알고 서비스를 개발 해야한다.
  • 기반기술을 중요시 해야한다.
  • 적절한 엔지니어링을 택해야 한다.
  • 클린코드가 모든 경우에 정답은 아니다.
  • 애자일은 만능이 아니다.
  • 오픈소스를 무분별하게 가져다 쓰는것 보다, 내부 동작 원리를 이해하고 용도에 맞게 사용해야한다.

    부족한 부분을 기여할 수 있는 프로슈머가 되어야 한다.

필자분께서 결론에 담아주신 내용이 가장 인상이 깊었습니다.

쥬니어와 시니어가 같은 일을 하고 같은 품질의 결과물을 만들어 낸다면
나이는 많고 연봉은 높은 시니어를 반길 이유가 없다.
시니어는 기반 기술에 대한 높은 이해를 바탕으로 쥬니어와는 다른 고품질의 결과물을 만들 수 있어야 한다.
그리고 쥬니어가 성장하고 본받을 수 있는 높은 기술력을 갖추고 리딩할 수 있어야 한다.
단순히 1년의 경력을 10번 반복한 시니어는 아무런 경쟁력이 없다.

2019년의 저는 얼마만큼 위의 여섯가지 키워드에 근접하였을까요

2019년의 저에 대해 되돌아 보았습니다.

자세히 보기

[Hexo]블로그 주소를 바꾸었어요!

이번에도 Gracefullight님의 제안으로, dev 도메인을 구매하기로 하였습니다.

기존의 아이디를 사용하여 khzero.dev를 쓸까 하다가,

이왕 구매하는거 깃허브 아이디도 바꾸고, 도메인도 맞춰서 사자!

라고 생각을 했었습니다…그때 멈췄어야 했는데요…

자세히 보기

[Hexo]TravisCI로 블로그 자동 배포시 겪은 문제

WARN No layout 자동 배포를 했더니 빈 파일들만 올라가요!!

한줄 요약 : theme 폴더를 레포지토리에 추가하시면됩니다.

아래의 내용은 해당 원인을 찾아가던 본인의 경험담입니다…


블로그를 직접 만드는데, 시간을 투자하는것 보다 포스팅을 하는게 더 가치 있겠다 싶어

현재 블로그를 Hexo + Github Page를 이용하여 운영하고 있습니다

기존에는 배포를 Hexo-cli를 이용하여 아래와 같은 명령어를 사용하여 배포 했습니다.

1
2
3
$ hexo clean
$ hexo generate
$ hexo deploy

쉘에서 해당 명령어를 작성하는것 또한 귀찮아져서 package.json 파일을 이용하여,

1
2
3
4
5
6
7
8
/*
* package.json
*/
{
"scripts": {
"deploy": "hexo clean && hexo d -g"
}
}

해당 스크립트를 작성해서 npm deploy명령어로 배포를 하고, 소스는 별도의 레포지토리에 관리하고 있었습니다.

자세히 보기

[Java]해당 클래스의 서비스는 어디서 주입되나요??

회사 업무중 스프링 서비스 코드 푸시를 하였습니다.

다른 팀원들에게

인터페이스를 파라미터로 정의한것은 확인하였는데,

혹시 해당 컨트롤러에 주입은 어디에서 되나요?`

라는 질문을 들었습니다.

1
2
3
4
5
6
@RestController
@Slf4j
@RequiredArgsConstructor
public class YourController {
private final YourService yourService;
}

위와같이 컨트롤러가 선언되어있었습니다.(물론 예제입니다.)

자세히 보기

[Java]@JsonProperty이 왜 동작을 안하지?

1
2
3
class anonymousDTO {
String whatYouWant;
}

대부분 자바의 변수를 생성할때 위와 같이 CamelCase를 많이 쓰는것으로 알고있습니다…..(본인이 잘못 생각하는것일 수 있습니다.)

필자 또한 위와 같이 camelCase를 사용하였으나 API 통신 및 응답을 리턴할때에는

hyphen uppercase가 필요했습니다.

자세히 보기

[Java]@AllArgsConstructor의 잘못된 사용

Consider defining a bean of type java.lang.String in your configuration.

스프링부트로 서비스를 개발중에 @Value 어노테이션을 이용하여 application.yml 파일에 저장해둔 환경변수에 접근하고 싶었는데,

아래와 같은 오류가 발생하였습니다.

1
2
3
Parameter 2 of constructor in com.hodory.v1.service.MyService required a bean of type 'java.lang.String' that could not be found.

Consider defining a bean of type 'java.lang.String' in your configuration.
자세히 보기

[Java]스프링부트 회원 기존 비밀번호 체크하기

회원 비밀번호 변경 로직을 작성중에 현재 비밀번호와 새 비밀번호를 입력받아,

기존 비밀번호가 맞는지 체크하는 로직을 넣고 싶었습니다.

1
2
3
4
5
6
7
8
9
10
String currentPassword = new BCryptPasswordEncoder().encode(request.getCurrentPassword());	

final User persistUser = userRepository.findUserByIdAndPassword(userId, currentPassword)
.orElseThrow(() -> new EntityNotFoundException("회원정보를 찾을 수 없습니다."));

if(!currentPassword.equals(persistUser.getPassword())) {
logger.info("changePassword is Not Equal Current Password");
return new ResponseEntity<>(UserRegisterResult.ERROR.getResponseBody(),
HttpStatus.FORBIDDEN);
}

new BCryptPasswordEncoder().encode(password);로 암호화 한 패스워드를 저장했기 때문에,

회원을 찾을때도 이렇게 하면 되겠다고 생각해서 위와 같은 코드를 작성하였는데,

테스트중 계속하여 EntityNotFoundException이 발생하였습니다.

디버깅으로 체크하였더니 String currentPassword = new BCryptPasswordEncoder().encode(request.getCurrentPassword()); 부분에서 매번 다른 비밀번호가 currentPassword에 들어갔습니다.

왜 다른 값이 나오는지 알고 싶어 BCryptPasswordEncoder 클래스 파일을 열어보았는데,

encode 부분에서 rawPasswordsalt값을 생성하여 두개의 값으로 패스워드를 해싱하고 있었습니다.

해당 클래스 파일안에 matches(CharSequence rawPassword, String encodedPassword)라는 함수가 있었고,

클래스의 인터페이스를 확인하였더니,

1
2
3
4
5
6
7
8
9
10
11
/**
* Verify the encoded password obtained from storage matches the submitted raw
* password after it too is encoded. Returns true if the passwords match, false if
* they do not. The stored password itself is never decoded.
*
* @param rawPassword the raw password to encode and match
* @param encodedPassword the encoded password from storage to compare with
* @return true if the raw password, after encoding, matches the encoded password from
* storage
*/
boolean matches(CharSequence rawPassword, String encodedPassword);

라는 주석을 확인하였고, 구현체가 아닌 인터페이스를 사용하고자
org.springframework.security.crypto.password.PasswordEncoder 를 의존성 주입하여,

1
2
3
4
5
6
7
8
final User persistUser = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException("회원정보를 찾을 수 없습니다."));
if(!passwordEncoder
.matches(request.getCurrentPassword(), persistUser.getPassword())) {
logger.info("changePassword is Not Equal Current Password");
return new ResponseEntity<>(UserRegisterResult.ERROR.getResponseBody(),
HttpStatus.FORBIDDEN);
}

이와 같이 수정하였고, 기대했던 결과대로 수행되었습니다.

아직 자바와 스프링이 많이 서툴러서 코드를 작성하는 시간보다 검색해보는 시간이 많아 더 어려운거 같습니다.

잘못된 부분이 있다면 코멘트 부탁드리겠습니다.

[JS]jQuery 없이 Selectbox에서 여러개 선택된 option의 값 추출하기

DOM을 다룰때 jQuery를 사용하면 매우 편리합니다.

물론 크로스 브라우저 이슈로도 제이쿼리를 많이 사용합니다.

하지만 DOM을 핸들링하기 위해서만 jQuery를 쓴다면, 편리함은 챙기겠지만 낭비 아닐까싶습니다.

저는 위와 같은 생각으로,

주로 DOM 핸들링에는 jQuery 의존성을 줄이고자 순수 자바스크립트를 사용하려 노력하고 있습니다.

또한, document.querySelectordocument.querySelectorAll 를 이용한다면,

jQuery에서 사용하던 복잡한 DOM Selector도 쉽게 사용할 수 있습니다.

1
2
3
4
5
6
<select id="user" name="user[]" multiple="multiple">
<option>선택</option>
<option value="foo">Foo</option>
<option value="bar">Bar</option>
<option value="baz">Baz</option>
</select>

위와 같은 Multiple SelectBox에서 여러개가 선택되었을 경우 값을 어떻게 가져올까요?

jQuery를 사용하면 $("#user").val()으로 짧고 간결하게 가져올 수 있습니다.

선택된 값이 없을 경우 null이 리턴되고, 선택된 값이 있으면 배열 안에 value 값이 담깁니다.

그렇다면 순수 자바스크립트로는 어떻게 표현할까요?

1
2
3
Array.from(document.querySelector("#user").selectedOptions, (item)=> {
return item.value;
});

document.querySelector("#user").selectedOptions를 사용하면 ID 값이 user인 DOM을 찾아 selected 된 옵션을 리턴하여줍니다.

해당 값에서 value 값만 뽑고싶다면 위와 같이 처리할 수 있습니다.

위와 같이 처리하면 jQuery를 사용할때와 다른점은 선택된 옵션이 없을 경우에는 []를 리턴하여 줍니다.

[PHP]Monolog와 함께 로그를 시작하자 - 1

PHP에서 로그 남기기

PHP에서는 주로 JS에서 console.log | console.debug 와 유사하게

var_dump | print_r | echo | sys_log | error_log 등 다양한 방식으로 로그를 남기고 있습니다.

Monolog란?

PSR-3을 준수하고 있는 로깅 프레임워크로,

거의 대부분의 로깅 플랫폼과 연결할 수 있는 핸들러(Slack, ElasticSearch, Mail, NewRelic 등)를 제공하고 있습니다.

RFC 5424(Syslog Protocol이라고 하는데 더 공부해봐야할 것 같습니다.)에 정의된 로그 레벨을 모두 지원하고 있습니다.

  • DEBUG(100)
  • INFO(200)
  • NOTICE(250)
  • WARNING(300)
  • ERROR(400)
  • CRITICAL(500)
  • ALERT(550)
  • EMERGENCY(600)
자세히 보기