Post about modifying crontab using ssh


Environment and Prerequisite

  • Linux
  • SSH(OpenSSH)
  • crontab


How

crontab

crontab [-u user] file
crontab [-u user] { -l | -r [-f] |	-e }
  • Give standard input(stdin) or filename after crontab command overwrite crontab content to that standard input(stdin) or file.
  • crontab -e cannot be used in ssh command so use above method.
  • Document says “The first form of this command is used to install a new crontab from some named file or standard input if the pseudo-filename ‘-‘ is given”.


Example

Scenario

  • Modify server’s crontab from current computer

Example

  • Check crontab content in server
# Check remote crontab
ssh twpower@157.230.234.230 "crontab -l"
0 10 17 * * /home/twpower/simple-script.sh
40 7 * * 1 python /home/twpower/run.py
  • Modify crontab content and apply it after copy crontab content to /tmp.
# Copy current crontab content to temporary file
ssh twpower@157.230.234.230 "crontab -l > /tmp/tmp-crontab-content"

# Add crontab content to temporary file
# Not only addition but also deletion and modification are possible
# Many variations are possible like using sed, tee or cat
ssh twpower@157.230.234.230 "echo '* * * * 1 python /home/twpower/run.py' >> /tmp/tmp-crontab-content"

# Apply new crontab using temporary file
ssh twpower@157.230.234.230 "crontab /tmp/tmp-crontab-content"

# Remove temporary file
ssh twpower@157.230.234.230 "rm -rf /tmp/tmp-crontab-content"
  • Check result
# Check remote crontab
ssh twpower@157.230.234.230 "crontab -l"
0 10 17 * * /home/twpower/simple-script.sh
40 7 * * 1 python /home/twpower/run.py
* * * * 1 python /home/twpower/run.py

Script

  • It can be made to script like below.
#!/bin/bash

# Copy current crontab content to temporary file
ssh twpower@157.230.234.230 "crontab -l > /tmp/tmp-crontab-content"

# Add crontab content to temporary file
# Not only addition but also deletion and modification are possible
# Many variations are possible like using sed, tee or cat
ssh twpower@157.230.234.230 "echo '* * * * 1 python /home/twpower/run.py' >> /tmp/tmp-crontab-content"

# Apply new crontab using temporary file
ssh twpower@157.230.234.230 "crontab /tmp/tmp-crontab-content"

# Remove temporary file
ssh twpower@157.230.234.230 "rm -rf /tmp/tmp-crontab-content"


Reference

올해의 회고


배경

매년 작성하던 회고라 올해도 한번 해보고 싶어서 쓴다. 처음에 쓸 때는 이게 무슨 도움이 될까 싶었지만 과거에 회고들을 읽어보니 감회가 새롭고 무슨 생각을 가지고 있었는지 떠올라서 스스로를 돌아보고 반성하기에 좋은거 같다. 아래는 지금까지 썼던 회고들 목록이다. 이제 나이도 어린 나이가 아니고 회사도 5년 넘게 다니고 있고 새로운 가족도 만들어서 여러가지로 올해는 의미가 새롭다. 이번에도 할게 많고 바쁘니 짧고 간결하게 쓰자.


목표는 이뤘나?

작년 회고에 정리했던 목표들을 올해 이뤘는지 궁금했다. 그래서 작년에 목표로 기록했던 것들을 가져와봤다.

개발시 코드에 대해 더 집중하면 좋겠다.

더 집중은 했지만 기대에 미치지 못한거 같다. 회사에서 아직 부족하지만 처음으로 Spring을 제대로 해보았고 코드에 조금 더 신경을 썼던거 같다. MVC 패턴에 따라서 코드를 작성하고 이제 간단한 Spring 서버의 경우 만들고 배포할 수 있을거 같다.

함수를 분리하거나 목적에 맞는 코드를 만들고 코드만 봐도 의미를 알 수 있도록 작성하려고 노력하였으나 여전히 코드가 길고 디자인 패턴이나 객체지향 프로그래밍 같은 부분을 제대로 녹이지 못한 부분이 아쉬웠다.

아래처럼 정리를 해볼 수 있을거 같다.

달성한 부분

  • MVC 패턴에 따라서 코드를 작성하려고 했다.
  • 약간의 객체지향적 코드 활용했다.
  • 긴 함수를 분리하고 의미를 알 수 있도록 작성하려고 노력했다.

아쉬운 부분

  • 상속이나 인터페이스를 활용한 객체지향적 코드의 작성이 부족했다.
  • 디자인 패턴과 같은 부분의 적용이 부족했다.
  • 작성한 코드의 의미를 알 수 없어서 일하는 동료에게 설명을 해야했다.
  • 코드가 너무 길어서 읽기 어려울 때가 있다.

노력은 했지만 여전히 아쉬운 부분이 많아서 내년에도 관련한 부분은 꼭 목표로 잡고 공부해야겠다.

집중을 더 하면 좋겠다.

공부나 일을 할 때 집중력이 약해서 썼었는데 여전히… 집중력은 없는거 같다. 유혹이 너무도 많다. 컴퓨터에 앉으면 뉴스, 유튜브, 게임 등등 많은 유혹들이 있다. 어찌보면 작년보다 더 집중력이 떨어진거 같다. 확실히 혼자만의 시간을 갖고 온전히 집중하는게 필요할거 같다. 집중을 해야 하는게 잘 진행된다!

자기 개발을 할 때 더 집중하면 좋겠다.

위랑 같은 이야기다… 어떤거든 할 때 집중을 더 해야할거 같다.

간단하더라도 작품이 있으면 좋겠다.

올해 서비스 개발을 위해서 Spring을 혼자 공부했고 예전부터 게임이 만들고 싶었어서 Unity를 좀 공부하고 작품을 아주 간단한걸 출시했다. 서비스를 출시한건 아니지만 만들고 싶은 게임을 간단하게 만들어서 출시하고 Admob도 붙여서 이 부분은 약간 목표를 이룬거 같다.

게임을 만들어서 사람들에게 감동있는 서사를 제공하고 싶기도 하고 의미 있는 내용을 전달하고 싶기도 하다.

현재 간단한 게임을 만들어서 출시했는데 업데이트를 꾸준히 해서 발전시켜 볼 예정이다. 그리고 추가적으로 만들고 싶은 게임이 2개 정도 더 있는데 시간을 내서 기획하고 개발해보고 싶다. 하나는 서사가 주된 게임이고 하나는 전달하고 싶은 메시지가 있는데 그걸 최대한 담아볼 생각이다.

별개로… 게임 개발을 하다보니 디자인을 직접하고 싶어졌다… 리소스를 직접 그려야하는데 이것도 공부해야한다.

정리하면 내가 만든 작품이 아주 간단하게나마 출시한게 있었고 이는 내년에도 가져가서 발전시킬 예정이다.

그리고 여전히 블로그는 시간 있을 때마다 열심히 작성하고 있다.


올해는 어땠나?

올해는 솔직히 정말 정말 정신이 없었다. 새로운 가족도 생기고 회사에 일도 바쁘고 개인 시간이 거의 없었다. 돌이켜보면 올해가 어떻게 지나갔는지도 모르겠다. 앞으로 점점 더 바빠지겠지?!

그래도 천천히 생각해보면서 올해 이룬거와 느낀거를 지금 순간에 생각나는대로 정리해봐야겠다.

전사에 관련한 회사의 업무

작년과 같이 회사 빅데이터 센터에 소속되어 있다. 회사의 Big Data 관련 업무를 진행하는 곳으로 작년에는 회사의 Data Silo를 해결하자는 취지가 주된 목적이었고 올해는 전사에 있는 서비스에 개인정보가 어떻게 있는지를 파악해서 개인정보 보호를 강화하는 목적이 강했다.

관련해서 작년에는 BDP라는 데이터 수집 플랫폼을 구축했고 올해의 경우는 앞선 BDP와 함께 개인 정보 보호 강화를 위한 플랫폼을 추가로 구축했으며 우리의 시스템에 여러 서비스들을 연결했다.

입사했을 때부터 회사에 제일 필요한 부분들 중 하나라는 생각을 했었고 그러한 일을 하고 있다는거에 여전히 기대와 뿌듯함이 있다. AI가 중요해지는 시점에 빅데이터는 없어서는 안될 그런 요소이기도 하니까 중요한 일을 센터가 맡고 있는거 같다. 이전에 연구소에 있을 때보다는 회사에 기여하는 느낌이 있다. 다만, 앞으로도 회사에서 우리를 계속 지원해줄지 걱정이 크다.

크게 봤을 때 다른 회사들에는 없는 시스템을 만들어가고 있고 개선해나가고 있는 거는 큰 장점이다.

회의감이 들기도 했던 업무

여러 서비스들을 시스템에 연동하면서 많은 회의감이 들기도 했다. 플랫폼 개발은 비교적 일찍 끝났으며 작년에 비해 많은 부분들을 자동화 및 개선했지만 여전히 사람의 개입이 필요했으며 여러 검증들을 하나하나 우리가 직접 해야하는 부분에서 회의감이 많이 왔다. 서비스 측이 틀리거나 고집 부린 부분들도 있는데 그러한 부분들을 엔지니어가 다 상대해야 해서 개발 시간이 좀 부족하기도 했다. 비유를 하자면 개발자가 아니라 단순히 기계나 ChatGPT도 할 수 있을거 같은 일을 했던거 같다.

조직 변경과 불안한 미래

입사할 때부터 같이 계셨던 임원분이 부사장을 끝으로 나가셨고 올해만큼 조직 변경에 불안했던적이 없었던거 같다. 인력 충원이 절실해 보이며 다른 파트와 R&R을 조금 더 명확하게 할 필요가 있는거 같다. 그리고 확실하게 1년은 빅데이터 센터가 있을 수 있을거 같은데 그 후에는 어떻게 될지 잘 모르겠다.

작년만큼은 아니지만 작은 성장

작년에는 Public 클라우드에 관해서 많이 배울 수 있었다. Private 클라우드만 구축하고 운영해봤기에 Public Cloud는 새로웠다. 데이터베이스(RDS, DynamoDB 등등), 보안과 인증(IAM, Federation 등등) 그리고 AWS와 GCP 사용법에 대해서는 작년에 꽤 많이 알게된거 같다.

올해는 사실 작년에 비해 클라우드를 많이 공부했는지는 모르겠다. Spring에 대해서는 그래도 약간 공부한거 같다. 위에 언급한대로 Spring과 서버에 대해서 조금 알게 되었다. 아직 많이 부족하지만 이제 서버 개발에 관련해서는 찾아보면서 개발을 할 수 있을거 같다.

그러한 부분 외에 코드에 대해서 그래도 이전보다 신경쓰면서 만든거 같다. 중복인 부분을 제거하거나 함수를 묶는 리팩토링도 좀 진행했고 가능하다면 상속과 같은 부분도 사용하려고 했다.

코드 작성 외에 태도가 하나 바뀐게 있는거 같다. 어떠한 일을 할 때 반복적이거나 수정을 할 수 있는 부분을 발견하면 어떻게 고칠 수 있을지 생각하는 버릇이 생겼다. 물론… 많은 부분들을 봤으나 시간상 적용하지 못해 발전했다고 할 수는 없다… 긍정적인 부분은 반복된 부분이나 내가 만든 API들을 사용시 불편한 부분들이 발견되면 수정하는 습관이 생긴거 같다. 예를 들어, 특정 프로세스 실행의 로그를 보고 싶다거나 수동으로 하나하나 실행하던 API를 하나의 API에 다 통합해서 실행하면 좋겠다와 같은 피드백을 받으면 생각해보고 적용했다. 관리의 측면에 있어서도 Slack 같은 도구에 문제가 있을 때 알림이 가는 부분은 필수인거 같고 다른 일하시는 분이 audit logging을 넣어주셨는데 이 중요성도 알게 되었다.

개발이나 지식적인 부분도 좀 늘었지만 태도나 생각하는 부분 그리고 문제를 보고 해결하고 싶은 부분이 좀 내적인 부분에서 변화하지 않았나 싶다.

꾸준한 블로그 운영과 작품 출시

살면서 당당하게 내놓을 수 있는 내 작품이 거의 없는데 블로그 운영을 꾸준하게 하고 있어서 내 스스로도 뿌듯하다. 이제 chatGPT가 나오고 구글에서 검색하는 부분이 줄고 다른 능력있는 분들의 블로그도 많아서 내 블로그나 글에 방문자 수가 줄어들고 있지만 꾸준하게 운영하고 작성한다는 부분에 있어서는 스스로 자랑스럽고 뿌듯하다. 당연히 앞으로도 꾸준하게 작성할 예정이다. 배운거나 느낀거나 반성한거나 어떤거라도 적을 예정이다.

블로그와 함께 돈을 벌거나 위에서 언급한것처럼 내 작품을 만들고 싶었는데 올해 아주 작게나마 게임을 만들어서 출시했다.

하루에 한명이 들어올까말까하지만 그래도 출시했다는거에 의의가 있고 이것도 꾸준하게 업데이트를 해볼 예정이다. 내년에는 위에 언급한대로 2개 정도 더 출시하면 좋겠다. 그리고 수익성도 좀 있을면 좋겠다.

꾸준하게 만들면서 부업은 계속 시도해볼 생각이다.

책임감

가족이 생겼고 챙겨야 할 가족이 늘어났다. 어깨가 무겁거나 하지는 않다. 어떻게 보면 행복한 책임감 같다. 어떤 행동을 할 때 더 조심스러워지고 신중해졌다. 이제는 단순히 나 혼자만을 위해 살 수 없고 이제는 가족들을 위해서라도 더 일해야한다. 그래도 부담보다는 이 책임감이 원동력이 되는거 같아서 좋다. 삶을 살 또 다른 이유가 생긱 느낌?!

살짝 아쉬운 점은 버는 돈이나 시간을 모두 나에게만 쓸 수 없다는 부분?이 아쉬울 때가 가끔 있지만 그래도 다른 부분들을 얻었으니 열심히 살아야겠다.

앞으로의 목표와 생각

예전에 심리상담 받았을 때 나는 남들보다 지나치게 우려와 고민이 많은편이었던게 기억난다. 그만큼 평소에도 많은 고민과 생각을 하고 산다.

자주 하는 고민은 여러가지가 있다. 내 미래, 우리 가족, 앞으로 만날 내 가족, 돈, 나라, 환경 등등 크고 작은 모든 걱정을 다 하는편이다.

작년에 작성했던 글을 읽어보니 내가 생각하는 나를 잘 쓴거 같다. 여전히 변함없는 내 목표들인거 같다. 아래 추가적으로 적어본다.


아쉽고 개선할 부분 그리고 목표

개발시 설계와 좋은 코드의 구현

위에 나온대로 개발시 설계를 UML과 같은 이론을 좀 적용해서 설계를 더 견고하게 했으면 하고 코드 작성시에도 더 좋은 코드를 작성하면 좋을거 같다. 여기서 좋은 코드라 하면 애매하지만 위에 나온 “아쉬운 부분”들을 해결하는데 초점을 두자.

선택과 집중

우선 순위 선정을 하고 정한건 확실하게 집중해서 빠르게 처리하자.

추가적인 작품 개발

블로그는 꾸준하게 쓰고 있고 2023년에 간단한 게임을 만들었지만 새해에는 더 재밌고 계획했던 작품을 만들고 싶다.

지속적인 성장과 공부

관심 있는 분야인 만큼 지속적으로 공부하고 정리하고 만들고 생각해야겠다. 새해에는 서버 개발과 코드를 많이 공부했으면 하며 회사 업무와 내 작품을 만들면서 더 공부 할 예정이다.


인생에 관해서

항상 글을 쓰고 목표는 거창하게 잡지만… 그걸 다 이루기는 어려운거 같다. 예전에 내 나이가 되면 많은걸 이뤘을줄 알았는데 아직 그에 미치지 못하는거 같다. 살다보니 운도 정말 중요한거 같기도 하다. 예전에는 노력만하면 어떤거든 다 할 수 있을줄 알았는데 이제는 운도 따라줘야 한다는걸 사회에 나오니 더 몸으로 느껴진다. 세상에 쉬운거가 없구나~

작년에 썼던 글을 보는데 웃긴 부분은 작년과 마음 가짐이나 생각이 똑같다. 그렇기에 그 내용 그대로 붙여 써본다.

어렸을때 31살이면 많은걸 이룰줄 알았는데 어디까지 왔는지 잘 왔는지 모르겠다. 어렸을때 그리고 자라면서 많은 목표가 있었던 거 같다. 위인전을 많이 읽어서 그런가 힘들게 버티고 발전해온 우리나라를 지키고 발전시키고 싶기도 했고 착한분들이 나쁜놈들한테 피해보는걸 보면서 어떻게 하면 저런 사람들이 세상에 없을 수 있을까와 같은 생각을 했고 어려운 사람들을 보면 돕고 싶기도 했고 세상에 기여하고 싶기도 했다. 아 물론! 지금도 동일하다. 나중에 아주 많은 시간후에 내가 죽기전에 내 삶이 “괜찮았다.”라고 생각하면서 앞선 목표들중 하나라도 이룬 모습을 가끔 꿈꾼다. 사람의 손길이 비교적 적은 동물들도 돕고싶다.

앞선 목표들도 중요하지만 내 삶에서 가장 중요한건 내 행복이다. 내 행복의 많은 부분은 가족과 사람들이라고 이전에도 적었었다. 앞선 어떤 목표들보다도 내 가족이 중요하다. 내 삶에만 집중하고 성공만을 바라보면서 열심히 살다가 강아지 동생을 못 챙겼던 부분이 있었고 가족과 더 많은 시간을 보냈었으면 하는 후회가 있다. 그 이후부터는 가족을 중요시하고 가족과 1분이라도 더 많이 보내려고 노력한다. 남은 시간이라도 내 가족을 잘 챙기고 더 많은 시간을 보내고 싶다. 어떤 일도 나와 내 가족보다 우선시 될 수 없는거 같다.

위에 두개의 문단은 여전히 유효한 내 생각이다. 그 아래 마지막 문단이 조금 바뀔거 같다. 올해는 가족이 생겼고 그렇게 같이 지내고 있다. 가족과 내 사람들을 잘 챙기는게 목표였는데 그걸 잘 하고 있는지는 잘 모르겠다… 정말 정신이 없는거 같다. 앞으로는 더 정신이 없을텐데 걱정이 이만저만이 아니다. 하고 싶은것들도 여전히 많다. 앞선 공부들과 목표를 포함해 주식, 여행, 운동, 영상 제작, 글쓰기, 독서, 취미, 게임, 봉사 그리고 사람 만나기 등등 하고 싶은게 많다.

그래도 다~ 할 수 있을거라 믿는다. 다들 그렇게 잘 살고 있으니까! 새해에는 가족도 나도 내가 챙기고 싶은 모두 다 더 잘 챙겨야겠다. 선택과 집중을 잘하면 된다.

그리고 아무리 어렵고 걱정이 많더라도 이제는 해야한다고 생각한다. 그리고 지금까지 해온걸 보면 충분히 할 수 있다.


마무리

매년 썼던 글들을 보니까 재밌다. 스스로 1년을 마무리하는 과정을 보는데 이상하게 뿌듯하고 진짜 한 해를 마무리하는거 같다. 내년에는 어떤 마음으로 어떤 느낌으로 글을 쓰고 있을지 궁금하다. 지금까지 해왔던것처럼 꾸준하게 열심히 살자! 감사하면서 겸손한 마음으로 열심히!

Retrospective of this year


Background

I decided to write a year-end retrospective which I used to do every year. At first, I wondered how it would be helpful but looking back at past retrospectives it brings back memories and allows me to reflect on what I was thinking at the time. It’s a good way to introspect and learn myself. Below is the list of retrospectives I’ve written so far. Now I’m not that young and having worked at the company for over 5 years and having a new family, this year feels like a special significance to me. There’s a lot to do and I’m busy so let’s keep it short and concise this time.


Did I achieve goals?

I just wondered I achieved goals in this year that I set last year. So I brought back the goals I wrote last year.

Focus more on code

I tried to focus more but it seems that I fell short of expectations. I’m still lacking at work, but I tried Spring properly for the first time, paying more attention to the code. I wrote code following the MVC pattern and now I feel confident in creating and deploying simple Spring servers.

I made efforts to separate functions and create code that suits the purpose. Write code in a way that its meaning can be understood by looking at it. However, the code still seems lengthy, and I feel a bit disappointed that I couldn’t fully incorporate aspects like design patterns or object oriented programming.

I think I can summarize it as follows.

Achieved

  • Tried to write code following the MVC pattern
  • Use a bit of object oriented programming
  • Made an effort to separate long functions and write them in a way that their meaning can be understood.

Regret

  • I lacked in writing object oriented code with inheritance or interfaces.
  • There was a deficiency in applying aspects like design patterns.
  • I had to explain the meaning of the code to my colleagues because it was not easy to know the meaning.
  • There are times when the code is too long so making it difficult to read.

I made efforts but there are still regrets. Therefore I’d like to set goals and continue studying above things next year as well.

Concentrate more

When studying or working, I used to struggle with concentration and it seems like I still do. There are many distractions. When I’m on the computer, there are news, YouTubes, games, and other things. Ironically, it feels like my concentration has decreased compared to last year. Clearly, I need my time and focus entirely. Tasks go well when I concentrate!

Concentrate more when doing self-development

Its same thing in above. I need to more concentrate whatever I do.

It is good to have my product

This year I studied Spring and Unity. I released very simple game made with unity. I haven’t officially launched a service, but I’ve created and released a simple game that I wanted to make. I’ve also integrated AdMob so I feel like I’ve achieved a bit of my goal.

I would like to provide people with a touching narrative through game and give meaningful content.

I have currently released a simple game and I plan to consistently update and improve it. Additionally, I have about two more games in mind that I want to create. I hope to find the time and opportunity to develop them. One will be primarily focused on narrative and the other will aim to encapsulate a message I want to convey as much as possible.

Separately, I have a mind to study about design. I need to draw game resource myself.

In summary, I have released a very simple product and I plan to continue developing it further into the next year.

And still I consistently write on my blog whenever I have the time.


How was this year?

Honestly this year was so busy. With the addition of a new family member and busy work at the company there has been almost no personal time. When I look back, I’m not even sure how this year has passed. Will it get even busier in future?!

I should take some time to reflect on what I’ve accomplished and experienced this year.

Work relates to the entire organization

I am in Big Data Center just like last year. Last year, the primary goal was to solve the company’s data silos. This year, there was a strong emphasis on understanding how personal information is present in services across the entire organization. The main object is to strengthen personal information protection.

In relation to this, last year, we established a data collection platform called BDP. This year, in conjunction with the existing BDP, we additionally built a platform to enhance personal information protection. We also connected various services to our system.

Since I joined the company, I have always thought that it is one of the most important parts in the company. There is still a sense of anticipation and pride in being involved in such work. In this era where AI is becoming increasingly important, big data is an indispensable element and it seems like our center is entrusted with significant responsibilities. There’s a feeling of contributing more to the company compared to my time in the research lab. However, there is a significant concern about whether the company will continue to support us in the future.

From a broad perspective, the significant advantage is that we are creating and improving a system that other companies don’t have.

Work that sometimes felt skeptical

I felt a sense of skepticism while integrating various services into our system. The platform development ended relatively early and we automated and improved many parts in system compared to last year. However human intervention was still needed. I felt a sense of skepticism while verifying each and every one of them. Sometimes there were mistakes or incorrect information from the service side which engineer had to deal with them. So that problem makes a lack of development time. To put it in an analogy, it felt like I was doing a job that even a machine or a ChatGPT could do.

Changes in company and uncertain future

Since I joined the company, an executive who had been with me left company as the vice president. I don’t think there has ever been a time when I was as anxious about company changes as like this year. It seems desperately necessary to recruit more members and clarify the roles and responsibilities with other parts. And it seems certain that the Big Data Center will exist for at least a year but I’m not sure what will happen after that.

Not as much as last year, but some growth

Last year, I was able to learn a lot about the public cloud. I only had experience about private cloud so public cloud was new to me. I feel like I learned quite a bit last year about databases(RDS, DynamoDB, etc.), security, authentication(IAM, Federation, etc.) and how to use AWS and GCP.

I’m not sure I studied the cloud as much this year as I did last year. However, I think I managed to study a bit about Spring. As mentioned above I’ve learned about Spring and servers. Although I’m still lacking a lot, I believe I can now develop server related things by searching on internet.

Besides that, I think I’ve been more trying to focus on code than before. I did some refactoring such as removing duplicates or wrapping functions and tried to use inheritance.

Other than coding, it seems like there’s been a change in attitude. I’ve developed a habit of thinking about how to improve duplicated or modifiable parts when I come across them in work. I’ve seen many areas to improve but due to time constraints I could not import it. A positive part is that I developed a habit of fixing inconvenient parts when finding repeated codes or APIs I’ve created. For example, when receiving feedback like wanting to see logs for a specific process execution or wishing to consolidate multiple manual API executions into one API, I think about it and apply it. From a management perspective, it seems essential for notifications to be sent when there are issues with tools like Slack. Additionally I’ve come to understand importance of audit logging.

While there has been some growth in terms of development and knowledge, I feel that aspects related to attitude, mindset, and the desire to identify and solve problems have changed a bit.

Consistent blogging and small release

Throughout my life I have very few works I can confidently show but I take pride in consistently maintaining and operating my blog. Due to the emergence of ChatGPT, a decrease in Google search traffic to my blog and other good blogs, the number of visitors to my blog has been decreasing. However I find a sense of satisfaction and pride in the steady operation and writing efforts to it. Of course I continue to write my blog about anything I learn, feel, reflect, or experience.

This year I wanted to earn money from my blog and create my own service or game as mentioned above. I developed a very simple game and released it.

Even though it’s uncertain whether one person will visit per day, there is a meaning in releasing it. I plan to consistently update it as well. Next year I hope to release around two more games, as mentioned above. It would be great if they could also be financially successful.

I plan to continue trying side projects while steadily creating something.

Responsibility

I have a new family and I have a responsibility to care them. It doesn’t feel burdensome rather it seems like a happy sense of responsibility. I’ve become more cautious and deliberate in my actions. Now I can’t live just for myself and I find myself working harder for the sake of my family. Despite the added responsibility, it feels more like a driving force than a burden. It’s like having a new reason to live.

Sometimes there’s a slight sense of regret that the money and time earned cannot be solely spent on myself. However considering the other things I’ve gained, I still feel the need to live diligently.

Future Goals and Thoughts

I remember when I had psychological counseling in the past, I was more prone to excessive worry and concern than others. I live my life with a lot of thought and worry.

I have various concerns that I often have. My future, our family, the family I will meet in the future, money, country, environment and etc. I worry about everything big and small.

Looking back at the post I wrote last year, I think I wrote well about how I am. It seems that my goals remain unchanged. I will write down at below parts.


Regret, improvement, and goals

Design and Implementation of Good Code in Development

As mentioned above, I would like to apply theories like UML to make my designs more robust during development and it would be good to write better code when writing code. Better code may seem ambiguous but let’s focus on resolving the “Regret” mentioned above.

Choice and Concentrate

Let’s make priority on what I have to do and focus on what’s been decided to do.

Additional works and releases

I’ve been consistently writing on my blog and I made a simple game in 2023 but in the new year I’d like to create more fun and meaningful works.

Keep growing and studying

As I interested in this field, I need to continue studying, organizing, creating, and thinking. In the new year, I hope to study a lot about server development and code. Also I have plan to learn more while working on my company projects and creating my own works.


About Life

Though I consistently set ambitious goals and write about them, achieving everything seems challenging. I thought that by my current age, I would have accomplished a lot. However it seems I haven’t quite reached that point yet. As I live, I realize that luck plays a significant role. In the past, I believed that with effort, one could achieve anything. However now that I’m out in the real world, I feel the importance of luck. There truly is nothing easy in this world.

Looking at the posts from last year, it’s amusing to find that my mindset and thoughts are still the same. Because of that I’ll try pasting the content just as it is.

When I was young, I thought 31 years of me would achieve many things but I cannot sure how many things I achieved. I think I had many goals when I was young and growing up. Maybe because I read a lot of great personal biography I’d like to protect and develop our country which has been struggling and developed in difficult circumstances, I’d like to help good people who suffered from bad people and I’d like to help and contribute to the world. Of course! It’s the same now. I sometimes dream of achieving any of my previous goals and thinking that my life was “OK” before I died so many times later. I’d like to help animals that receive relatively little human touch as well.

The previous goals are important but the most important thing in my life is my happiness. Most of my happiness is from my family and my people. My family is more important than any previous goal. I regret that I could not care for my brother(dog) and family because I just cared for myself and focused on my success. After that, I’m trying to spend even one more minute with my family. I’d like to take care of my family and spend more time with them. Nothing is more important than my family.

Those two paragraphs above still reflect my current thoughts. The last paragraph below, however, seems to have changed a bit. This year I made a new family and we’re living together. My goal was to take good care of my family and loved ones, but I’m not sure if I’m doing that well… It feels truly hectic. Future will be difficult more but I still have many concerns. I still have many things I want to do, including previous studies and goals, as well as interests such as stocks, travel, exercise, video production, writing, reading, hobbies, game, volunteering, and meeting people, and more.

Even though I believe that I can do it. Everyone else is living so well! In the new year, I’d like to take better care of my family, myself, and everything I want to take care of.

And no matter how difficult and worrisome it may be, I now believe that it’s time to do it. Looking at what I have accomplished so far, I am confident that I can do it sufficiently.


Conclusion

It’s interesting to read the posts I’ve written each year. Somehow watching the process of wrapping up the year feels strangely satisfying and truly signifies the end of a year. I’m curious about the mindset and feelings with which I’ll be writing next year. Let’s continue to live diligently with consistency, just as I have done so far! Gratefully and humbly, let’s work hard!


환경

  • Java


방법

여러가지 방법이 있는데 아래에서는 2가지 방법을 소개한다. 예시와 함께 사용 방법을 테스트를 통해 정리했다. JPQL에서 LIMIT 1을 사용할 수 없어서 조사하다가 찾은 내용을 정리한다.

  1. JPA Query Methods를 이용한 방법
  2. Pageable을 이용한 방법


사용법과 예시

build.gradle

  • 아래 build.gradle은 개인 프로젝트 환경마다 다를 수 있다.
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.5'
	id 'io.spring.dependency-management' version '1.1.3'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '21'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	compileOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
	testImplementation 'org.junit.jupiter:junit-jupiter'
	testImplementation 'org.junit.jupiter:junit-jupiter-api'
	testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}

tasks.named('bootBuildImage') {
	builder = 'paketobuildpacks/builder-jammy-base:latest'
}

tasks.named('test') {
	useJUnitPlatform()
}

UserEntity

  • 예시로 사용할 Entity
package com.example.practice.entity;

import jakarta.persistence.*;
import lombok.*;


@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")})
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String gender;

    @Column(nullable = false)
    private String type;

    @Column(nullable = false)
    private String email;

    @Column(nullable = false)
    private String password;
}

UserRepository

  1. JPA Query Methods를 이용한 방법
    • 정해진 메소드 이름 규칙에 따라서만 만들면 된다.
    • 해당 방법의 경우 아래 예제를 보거나 공식 문서를 보면 쉽게 찾을 수 있다.
  2. Pageable을 이용한 방법
    • 예제에서는 @Query를 사용했으며 List로 반환하고 Pageable을 끝에 넘겨주면 된다.
    • 사용 방법은 아래 테스트에서 볼 수 있다.
package com.example.practice.repository;

import com.example.practice.entity.UserEntity;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {
    // 1. JPA Query Methods를 이용한 방법
    UserEntity findFirstByGenderAndTypeOrderByIdDesc(String gender, String type);

    // 2. Pageable을 이용한 방법
    @Query("SELECT u FROM UserEntity u WHERE u.gender = :gender AND u.type = :type ORDER BY u.id DESC")
    List<UserEntity> findFirstByGenderAndTypeOrderByIdDescUsingPageable(@Param("gender") String gender, @Param("type") String type, Pageable pageable);
}

UserRepositoryTest

  1. JPA Query Methods를 이용한 방법
    • 하단의 예시처럼 메소드를 호출하면 된다.
  2. Pageable을 이용한 방법
    • PageRequest.of(0, 1)를 넘겨준다.
    • PageRequest.of(0, 1): 첫 번째 페이지에서 한 개의 결과를 가져오도록 하는 설정
package com.example.practice.repository;

import com.example.practice.entity.UserEntity;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.Arrays;
import java.util.List;

@ExtendWith(SpringExtension.class)
@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void findFirstByGenderAndTypeOrderByNameDescTest() {

        List<UserEntity> userEntities = Arrays.asList(
                UserEntity.builder().
                        email("test1@test.com")
                        .username("testUsername1")
                        .password("testPassword1")
                        .gender("male")
                        .type("A")
                        .build(),
                UserEntity.builder()
                        .email("test2@test.com")
                        .username("testUsername2")
                        .password("testPassword2")
                        .gender("female")
                        .type("A")
                        .build(),
                UserEntity.builder()
                        .email("test3@test.com")
                        .username("testUsername3")
                        .password("testPassword3")
                        .gender("male")
                        .type("A")
                        .build(),
                UserEntity.builder()
                        .email("test4@test.com")
                        .username("testUsername4")
                        .password("testPassword4")
                        .gender("male")
                        .type("B")
                        .build(),
                UserEntity.builder()
                        .email("test5@test.com")
                        .username("testUsername5")
                        .password("testPassword5")
                        .gender("female")
                        .type("A")
                        .build()
        );
        userRepository.saveAll(userEntities);

        // 1. JPA Query Methods를 이용한 방법
        UserEntity resultUser = userRepository.findFirstByGenderAndTypeOrderByIdDesc("male", "A");
        Assertions.assertEquals("test3@test.com", resultUser.getEmail());

        // 2. Pageable을 이용한 방법
        List<UserEntity> resultUsers = userRepository.findFirstByGenderAndTypeOrderByIdDescUsingPageable("male", "A", PageRequest.of(0, 1));
        Assertions.assertEquals("test3@test.com", resultUsers.getFirst().getEmail());
    }
}


참고자료


Environment and Prerequisite

  • Java


Method

There are various methods and here I introduce two methods with examples. The post was written after searching inability of using LIMIT 1 in JPQL.

  1. Using JPA Query Methods
  2. Using Pageable


Usage and Example

build.gradle

  • Below build.gradle can be different per project setting.
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.5'
	id 'io.spring.dependency-management' version '1.1.3'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '21'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	compileOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
	testImplementation 'org.junit.jupiter:junit-jupiter'
	testImplementation 'org.junit.jupiter:junit-jupiter-api'
	testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}

tasks.named('bootBuildImage') {
	builder = 'paketobuildpacks/builder-jammy-base:latest'
}

tasks.named('test') {
	useJUnitPlatform()
}

UserEntity

  • Example Entity
package com.example.practice.entity;

import jakarta.persistence.*;
import lombok.*;


@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = "email")})
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String gender;

    @Column(nullable = false)
    private String type;

    @Column(nullable = false)
    private String email;

    @Column(nullable = false)
    private String password;
}

UserRepository

  1. Using JPA Query Methods
    • Create method name according to naming rule.
    • Naming rule can be easily found in official document or below example.
  2. Using Pageable
    • An example uses @Query and returns a List and Pageable is passed to method at the end.
    • The usage can be seen in the test below.
package com.example.practice.repository;

import com.example.practice.entity.UserEntity;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserRepository extends JpaRepository<UserEntity, String> {
    // 1. Using JPA Query Methods
    UserEntity findFirstByGenderAndTypeOrderByIdDesc(String gender, String type);

    // 2. Using Pageable
    @Query("SELECT u FROM UserEntity u WHERE u.gender = :gender AND u.type = :type ORDER BY u.id DESC")
    List<UserEntity> findFirstByGenderAndTypeOrderByIdDescUsingPageable(@Param("gender") String gender, @Param("type") String type, Pageable pageable);
}

UserRepositoryTest

  1. Using JPA Query Methods
    • Call the method as shown in below.
  2. Using Pageable
    • Pass ```PageRequest.of(0, 1)``.
    • PageRequest.of(0, 1): Configuring to retrieve one result on the first page
package com.example.practice.repository;

import com.example.practice.entity.UserEntity;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.Arrays;
import java.util.List;

@ExtendWith(SpringExtension.class)
@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void findFirstByGenderAndTypeOrderByNameDescTest() {

        List<UserEntity> userEntities = Arrays.asList(
                UserEntity.builder().
                        email("test1@test.com")
                        .username("testUsername1")
                        .password("testPassword1")
                        .gender("male")
                        .type("A")
                        .build(),
                UserEntity.builder()
                        .email("test2@test.com")
                        .username("testUsername2")
                        .password("testPassword2")
                        .gender("female")
                        .type("A")
                        .build(),
                UserEntity.builder()
                        .email("test3@test.com")
                        .username("testUsername3")
                        .password("testPassword3")
                        .gender("male")
                        .type("A")
                        .build(),
                UserEntity.builder()
                        .email("test4@test.com")
                        .username("testUsername4")
                        .password("testPassword4")
                        .gender("male")
                        .type("B")
                        .build(),
                UserEntity.builder()
                        .email("test5@test.com")
                        .username("testUsername5")
                        .password("testPassword5")
                        .gender("female")
                        .type("A")
                        .build()
        );
        userRepository.saveAll(userEntities);

        // 1. Using JPA Query Methods
        UserEntity resultUser = userRepository.findFirstByGenderAndTypeOrderByIdDesc("male", "A");
        Assertions.assertEquals("test3@test.com", resultUser.getEmail());

        // 2. Using Pageable
        List<UserEntity> resultUsers = userRepository.findFirstByGenderAndTypeOrderByIdDescUsingPageable("male", "A", PageRequest.of(0, 1));
        Assertions.assertEquals("test3@test.com", resultUsers.getFirst().getEmail());
    }
}


Reference