2023년 상반기 회고

2023년 상반기...
아등바등 살았지만, 얻은 게 무엇인지 정리 해보려니 굵직한 것은 없는 듯하다.
본격적인 여름에 돌입하는 시점에서 여러 번의 실패 끝에 방황이 다시 시작되었고 마음을 다잡기 위해서 회고하기로 결심했다. 시기적으로 지금 해야 할 시점이기도 하고.

연초에 세운 목표의 중간 점검

Java, Spring 깊게 공부하기

어느 정도는 노력했지만, 사실 목표에 크게 닿지는 못했다고 평가한다. 토비의 스프링도 1권만 1독했을 뿐 2권은 시작하지 못했고, 그 외에도 Java나 Spring의 기초이론을 복습했지만, 면접에서 제대로 설명하지 못했기 때문에 떨어졌으리라 짐작한다. 그래도 미약하게나마 Java 11과 Spring Boot, Spring Batch로 프로젝트를 하면서 이전에는 사용하지 않았던 방식의 업무도 해결했으니 어느 정도는 했다고 볼 수 있지 않을까? 기회가 된다면 코틀린도 겉핥기를 한번 해야겠다.

인강 및 프로그래밍 책 읽기

우선 작년에 결제한 알고리즘 강의를 드디어 완강했다. 사실 한번 봤다고 머리에 다 남는 것은 아니기 때문에 추가적인 공부가 필요한 상태다. 다만 지금 공부할 것들이 너무 많다고 판단되는 시점에서 어떤 것을 선택하고 집중해야 할지 정하지 못했다. 다기망양의 시대에서 회사마다 원하는 스펙이 다르고 그렇다고 한 회사만 노리고 그 스펙에 맞춰 공부한다는 건 말이 안 된다고 생각하기 때문에 이것저것 얉게라도 공부하는 게 맞는지도 모르겠다. 그래도 결국 모든 회사는 잘하는 사람을 원한다는 사실은 변함없을 테니 중요한 과목이나 기술은 기초지식을 탄탄히 해둬야 한다는 사실은 변함없을 테지. 다양한 스택이 필요해 보여 이것저것 인프런에서 결제해 둔 강의가 많은데 생각해 보니 완독한 강의가 몇 없다는 것을 알고, 일단 김영한님의 JPA 로드맵을 3/4분기 혹은 하반기 동안 완강하기로 결심했다.
독서는 많이 했다고는 자신 있게 말할 수 없지만, 출퇴근 시간에 꾸준하게 보고 있다고는 자신할 수 있다. 토비의 스프링, 헤드 퍼스트 디자인 패턴, TDD 실천법과 도구, 클린코드, 읽코좋코, 이펙티브 자바, 디자인 패턴. 6개월 동안 6권은 읽은 듯. 물론 이 역시도 한 번에 소화할 수 없는 지식들 이기 때문에 1 독이라 표현하고 추가로 2독, 3독이 필요하다고 생각한다. 어쨌든 미약하지만 출근길 독서의 습관은 잃지 않도록 해야겠다.

이직

아, 요즘의 나를 정말 무기력하게 만드는 키워드다.
"몇 곳이나 지원했는지"까지 밝히면 내가 너무 무능해 보이니 밝힐 수 없지만 정말 많이 떨어졌다. 물론 개중에 붙었지만 건방지게 입사 거절을 한 회사도 서너 곳 있지만, 코테나 1차 면접에서 떨어진 곳들이 누가 봐도 좋은 기회를 놓친 것이라 속이 많이 쓰리다. 그리고 부끄럽게도 서류에서 떨어진 곳도 많다.
기회는 준비된 사람만 잡을 수 있는 것이라고, 내가 아직 미흡한 점이 많기 때문에 목표를 이루지 못한 것으로 생각하지만, 그래도 정말 무기력해지는 것은 어쩔 수 없다. 작년 8월 말부터 이직 준비와 시도를 시작했다. 나름의 성과나 결과랄 것이 제대로 나오는 데 시간이 걸리기 마련인 것은 잘 알고있다. 이런 성취감이나 노력의 결과가 보이지 않는 시기가 있다. '아주 작은 습관의 힘'이라는 책에서는 이 구간을 낙담의 골짜기라로 표현한다. 나는 이 낙담의 골짜기를 나름 잘 견뎌내는 사람이라고 생각했는데, 올해는 정말 쉽지 않은 듯. 의지를 다시 다잡기 위해 조만간 이직 결심 사유와 내가 원하는 회사에 대해 정리를 해봐야겠다.

여행

분기에 한 번 여행이라는 키워드는 잊은 지 오래됐다. 대신 분기에 한두 번 문화생활은 하는 중. 전시나 공연을 보면서 잠시나마 낯선 경험과 노력에 대한 영감, 의지를 좀 다지게 되는 계기가 되었다.

운동과 건강

연초에 건강에 대한 계획이 있었다는 걸 잊고 있었다. 물론 틈나는 데로 집 앞 공원에서 산책 겸 러닝을 했지만, 작년에 세운 다이어트 목표는 저 멀리 사라지고 오히려 6키로 가량 쪘다...
연초에는 틈나는 데로 운동이라 목표를 설정했지만. 올 하반기는 주 3회 러닝 겸 산책을 필히 해야겠다. 러닝을 할 때 여러 생각들도 정리되고 건강도 챙기고 모로 가도 건강이 제일 중요한 게 아니겠는가...


그 외 주저리

인생이 아직 안정기에 접어들지 못했다는 이유로 후순위로 미뤄두었던 것들이 많다.
내 자신의 건강과 운동, 가족과 주변 친구들 혹은 잊었던, 새로운 인연들에도 노력을 기울여야겠다고 생각을 하게 됐다. 내한 공연을 챙겨 가게 된 계기가 "내가 혹은 아티스트가 언제 갑자기 다른 세상으로 건너갈지 모르니 기회가 있을 때 공연에서 그 바이브를 느껴보자"라는 생각에서였다. 물론 이 생각에서부터 시작된 생각은 아니지만 가족과 주변 사람들 또한 언제까지나 내 옆에 있진 않을 테니 함께 좋은 추억을 만들어 두는 것이 좋겠다고 생각하게 되었다.

인생의 안정기가 아니라는 이유로 연애도 쉬고 있는데, 인간관계에 대한 여러 생각이 스처 지나가고 내 남은 젊음을 생각했을 때 연애도 후순위로 미뤄둘 수 없는 문제라는 결론이 나왔다. 솔직히 누군가 새로운 사람을 만나 서로의 베스트프렌드가 되는 게 쉽지는 않아서 자신은 없고, 조심스러운 성격 때문에 누군가랑 친해지기도 쉽지 않은데, 뭐든 노력하지 않고 얻을 수 있는 것은 없기 때문에 조금의 노력을 해보기로 했다. 일단 사람들이랑 친해지는 것부터...

옷을 좀 그만 사야겠다. 아무래도 회사가 옷 가게다 보니, 특히나 내가 좋아하는 브랜드에서 일을 하게 되니 자꾸 소비하게 된다. 쇼핑중독의 원인은 아무래도 심리적으로 불안함(이직이 안됨)과 과도한 스트레스에 의한 통제력 상실이다. 이미 필요 이상의 옷을 샀고, 줄어든 통장과 넘쳐나는 옷장을 보니 쇼핑과 헤어질 결심을 세웠다.

취미 생활을 좀 해야 겠다는 생각을 하게 됐다. 몇 해 전까지만 해도 혼자서라도 매달 영화 한 편을 보기도 했고, 주말에는 밤세워 미드나 영화를 보기도 했고, 사진을 찍으러 나가기도 했었는데, 그런 취미활동이 삶의 우선순위를 바꾸다 보니 많이 줄었다. 어떻게든 취미생활을 좀 해야 삶의 활력이나 질이 높아지지 않을까? 전시/공연/영화 감상이라도 월 1회는 챙겨야겠다.

MBTI가 ENFJ에서 ENTJ로 바뀌었다. 물론 16분법의 통계가 한 사람의 모든 걸 알려주지는 못하지만, 신빙성 있는 통계라고 생각한다. 일을 하면서 좀 더 효율적으로 해결할 방법을 생각하느라 T로 변하게 된 것이 아닐까 하는 생각을 했는데, 다른 개발자 커뮤니티에서도 많은 F였던 사람들이 T로 변했다고 하더라. 근데 사실 나는 예전부터 E/I, T/F는 자주 오락가락했다. 그리고 그 두 가지보다는 N에서 S로 바뀌었으면 하는 바램이 있다.
김연아 선수의 옛 인터뷰 중 '무슨 생각을 해.. 그냥 하는 거지'라는 말처럼 무언가를 할 때 생각이 너무 많으면 내 목표가 흔들리거나 의지가 꺾이는 등의 사고(accident)가 발생하는 것 같다. 생각이 많아서 이직/공부에 더 깊이 들어가지 못하고, 인간관계에 있어서 너무 조심해지는 것 같다. 이 글은 그 생각들의 타래를 끊기 위해 결론을 짓는 행동이기도 하다.

뭔가 얘기할 주제가 1가지 더 있었던 것 같은데 지금 생각이 안 난다.

 

 
728x90
반응형

'커리어 디버깅' 카테고리의 다른 글

2023년 회고  (0) 2023.12.30
[면접 회고] 20230530 COY 면접 회고  (0) 2023.05.30
[면접 회고] 20230420 EI사 1차  (0) 2023.04.20
[면접 회고] W사  (0) 2023.02.27
[면접 회고] C사  (0) 2023.02.27
  • 4/29일 지원(30일 마감), 5/17 서류 합격 통보, 5/30 1차 면접 진행.
  • google meets 비대면 면접.
  • 호전적인 인상의 면접관님.

지원 ㄱㅅ
면접 전 설명

  • 30~50분 소요
  • 질문의 답이 길어지거나 주제에서 벗어난다면 끊을 것으로 양해
  • 개인적인 질문이 있을 경우 편하게 끊어도 된다

간단한 자기소개

"읽기 좋은 코드가 좋은 코드다"라는 개발 철학에 빠져 문제 해결에 있어 유지보수성 향상과 클린 코드에 가치관을 둔 4년차 개발자입니다.
Java, Spring 환경의 BE 운영/고도화/신규개발을 수행했고, 그 과정에서 만난 코드는 대체로 절차지향적이고 하드코드가 많으며, 중복이 많은 스파게티 코드였습니다. 저는 과업을 수행하는 과정에서 이러한 코드들의 구조를 개선시켰고, 유지보수성을 향상시켰습니다.
또한, 절차지향적인 코드를 작성하는 팀원들을 객체지향적 프로그래밍을 하도록 이끌어낸 경험도 있습니다.
JD 상에 기술 부채 해결과 레거시 코드 개선 및 신규 아키텍쳐에 적용이라는 키워드가 있었는데, 제가 잘 할수 있는 일과 도전해보고 싶은 일이라고 생각해 지원하게 되었습니다.

자사몰이나 앱 사용 해 봤느냐

ㅇㅇ 레벨 3다.
이거 저거 샀다. (구체적으로 명시했음, 글에 작성하면 어딘지 쉽게 알 수 있어서 자체 필터링.)

이건 내가 고쳐볼 수 있겠다 싶은 기능?

앱에서 픽업 기능을 사용하면 종종 튕기는 현상이 있었다. 기회가 주어진다면 개선해 보고싶었다.
그리고 PC 사용을 더 많이 하는 편인데, 개발자 창을 열어보면 불필요한 로그나 콘솔 에러가 좀 있고, 주석상에 어뷰징이 가능할 것 같은 내용이 있었다.
이런 점들을 개선 시켜 보고 싶었다.

스프링 부트 사용?

업무적 사용은 안했고, 공부할 때 써봤다.

그냥 스프링과의 차이는 어떤게 있다고 느꼈는가?

xml 설정은 읽기 편하지 않은 책을 읽는 느낌이고, 옵션을 넣을 때 <>안에 넣어야하는 값과 하위에 넣어야 하는 갑의 구분이 쉽지 않다.
톰캣이 내장되어 있기 때문에 개발 환경과 실행환경의 차이를 줄일 수 있는 게 장점이였던 것 같다.

DI에 대해 설명해 달라

Dependency Injection 의존성 주입. 각각의 클래스 의존 관계를 IoC 컨테이너가 자동으로 주입하는 것.
의존 관계 주입을 개발자가 관리 하지 않기 때문에 비즈니스 로직에 집중할 수 있는 장점이 있다.
의존성 주입은 Service, Repository, Autowired, Component 어노테이션을 사용할 수 있다.

의존성 주입을 할 때 Bean을 만드는데, bean에 대해 설명하라

어....(버벅버벅)...
IoC 컨테이너에게 관리를 위임하는 객체를 Bean이라고 볼수 있다.
IoC 컨테이너는 이 빈의 객체 생성부터 의존성 주입, 초기화, 소멸 등 라이프 싸이클을 관리한다.
(틀린 듯)

Bean의 디자인 패턴은 무엇인가?, 스프링은 보통 이 디자인 패턴으로 되었다고 한다.

bean 객체는 싱글톤으로 객체가 생성이 되고, bean 생성시 factory pattern도 사용한다고 알고 있다.
(틀린 듯)

AOP와 함께 따라다니는 3총사 Filter Interceptor AOP의 실행 순서를 말해보라

request -> filter.doFilter -> dispatcher -> interceptor.preHandle -> controller -> aop -> service -> aop.around -> controller -> interceptor.postHandle -> dispatcher -> filter.doFilter -> response

그 셋의 차이?

셋다 어플리케이션에서 자주 사용되는 로직을 분리하여 처리하는 기능을 제공.
filter는 보통 xss 보안을 위해 사용한다.
interceptor는 로그인, 권한, 로깅, 세션 처리등을 전담한다. aop의 기능을 흉내낸다고 볼 수 있다.
aop는 핵심 로직 사이에 끼어있는 횡단의 관심사, 공통의 로직을 분리해서 모듈화 한것으로....
(말 잇지 못함)

XSS는 무엇인가?

크로스 사이트 스크립트 공격으로, 게시물에 sript 태그를 넣어 다른 사용자에게 공격을 하는 것이고. 이런 script 태그를 막기 위해 xss 방어를 한다.

어떻게?

보통은 <>lt, gt로 치환을 많이 한다.
(원하는 대답은 이게 아닌 것 같은데, 아는게 이것...)

SI 할때 게시판에 특수 문자를 넣었을 때 깨진다 라는 인입을 받으면 어떻게 대처했는가?

예전 기억이라 흐릿한데, filter를 테스트 해서 수정한 기억이 있고. doFilter의 치환을 직접 작성하기도 하지만 naver lucy로 적용한 적도 있다.
(이것도 옳은 대답이 아닌 듯)

Transaction 요소 4가지 설명해 달라

아..(버벅)
독립성과 원자성 등등이 있었던 걸로 기억 나는데 잘 기억이 안난다.
(면접관님 호탕하게 웃음)

RESTfull API가 어떤건지 설명해봐라

Representation state transfer의 약자로 http 프로토콜을 그대로 이용하며 웹의 장정을 이용하고, URI를 통해 대상 객체를 지정, get, post, put, delete의 http method 이용해서 해당 자원의 처리를 하는 방법을 의미한다.

그럼 API 설계를 했을 텐데, RESTfull API 설계 원칙 몇가지를 대봐라

  • /는 계층관계를 나타낸다.
  • uri의 가독성을 위해 _는 지양하고 -를 지향해야 한다.
  • uri는 소문자를 권장한다.
  • 리소스 간의 연관관계가 있다면 /{메인 리소스}/{리소스 id}/{연관 리소스}로 표현한다.

http 응답 코드와 의미를 아는대로 불어라

  • 200
  • 302, 303, 304
  • 401, 404, 415, 405
  • 500

http method 불어라

  • get, post, put, delete
  • trace, option이라는 것도 있는 것으로 알고 있다.

사용 목적은?

  • get select
  • post insert/update
  • put update
  • delete delete

공공기관에서는 put, delete 사용하지 말라고 들었을 텐데 왜그런지 아느냐?

(예전 타사 면접에서 이 얘기 했다가 그게 말이 되냐는 소릴 들었던 경험이 있어서 오히려 반가웠음.)
정확한 기억은 아니지만, web server에 따라 put, delete를 was로 요청이 가는게 아닌 web server가 구종되는 OS 자원을 실제로 삭제하거나 데이터를 넣는 기능이 있다.

해당 내용에 대해서 apache, nginx에서 put, delete를 막는 방법을 아느냐?

흐릿한 기억으로는 설정파일에 allow, not allow로 요청자의 ip나 경로에 따라 해당 메소드를 허용/비허용하는 설정을 했었다.
(왜 이런 것 까지 해봤니?)

MyBatis에서 #과 $의 차이점이 무엇인가?

일반적으로는 #을 사용,
#은 string일 경우 ''를 붙여주고, number type인 경우에는 ''가 없이 쿼리를 만들어 준다.
$를 사용할 경우에는 type 상관 없이 주어진 객체를 '' 없는 스트링으로 넣어주는 것으로 알고 있다.
(정확한 차이를 대답한 것이 아니라 생각됨.)

보안팀에서 $를 사용하면 사용하지 말라고 하지 않던가?

재직하던 곳에서는 조직이 그렇게 나뉘어 져 있지 않아서 그런 일은 없었다.
다만 $를 사용하면 SQL Injection 문제가 있기 때문에 #을 사용해야 하는 것으로 알고있다.

DB는 어떤 것 사용해봤는가?

Oracle 주로 사용, mysql, PostgreSQL, Cubrid, Tibero

index 개선했다고 했는데, index가 어떤 원리기 때문에 쿼리를 개선해주는가?

DB가 데이터를 저장할 때 B+tree로 데이터를 저장하는데, tree와 LinkedList에서 index를 가지고 있는데...
(많이 딜레이 걸림, 대답을 안한 것과 같네)

oracle hint에 대해서 얘기 해봐라

optimizer가 hint를 참조해서 어떤 테이블을 드라이빙 테이블로 사용할지, 어떤 index를 사용할지에 대해서 지정할 수 있다.

index가 많으면 성능향상?

그건 아니져!!
index 자체로도 리소스이기 때문에 적절한 인덱스 설정이 필요하다고 알고 있다.

힘들었던 프로젝트를 꼽으면?

최근에 3자물류 연계, 오래전으로 돌아보면 첫회사에서 같은 팀의 다른 직원들 업무까지 관여를 많이 해야했던 순간이 힘들었다.

어떻게 문제를 해결했나? 자기 일도 아닌 것도 책임 져야 했을 텐데,

스트레스 받는 일이기도 하지만, 신입 직원 같은 경우에는 시간을 투자해서 진행상황 체크와 조언을 했고, 비슷한 연차의 직원 업무같은 경우에는 자율적으로 두고 주간회의를 통해 관리를 했다.

지금도 메니징을 하는 것인가?

아니다, 첫회사고 딱 한 프로젝트만 그런 역할을 했었다.

그럼 후임자를 독려하면서 일을 진행하다 보면 딜레이가 생길수도 있고, 상사와의 갈등이 생길 수도 있는데 어떻게 해결 했는가?

이 경험이 제가 2년 전 한번 겪은 경험이라 경험이 풍부하지 않고 기억이 흐릿하기도 한데, 그때 당시에는 최대한 팀원들을 독려하고, 간식거리를 사준다던지 조언을 좀 더 하는 방향으로 진행했다.
상사에게는 주기적 보고와 어떤 방법으로 해결하겠다와 같은 제시도 하고 했었고 책임지고 혼나는 일을 담당했다.
(면접관님 웃음)

최근에 관심있는 기술 트렌드가 있느냐? 우리가 신기술을 많이 사용해서 트렌드에 민감한 편이다. 그리고 그런 정보는 대게 어디서 얻느냐.

트렌디한 기술을 실제로 사용하진 못했고 블로그나 같이 공부하는 그룹에 동료들에게 정보를 얻는 편.
최신 기술은 아니지만 ELK나 상품 전시 도메인의 꽃은 검색이기 때문에 ES를 사용해보고싶다.
(코틀린을 대답할껄..)

스트레스 관리는 어떻게 하느냐?

잠을 좀 많이 자는 편이고, 영화 보는 것이 취미라 퇴근하고 혼자 영화를 본다던지, 주말에 친구들 만나서 보드게임을 하는 등으로 스트레스 해소를 하고있다.

  • 복지중에 영화 할인있는데 좋겠네

궁금한 것?

  • 합격하게 된다면 어떤 기술을 미리 학습 해 가는게 도움이 될것 같으냐?
    • 현재 코틀린으로 마이그 중이기 때문에 코틀린과 스프링 부트가 좋다.
  • 그럼 이전에 해결했던 이슈나 앞으로 해결해가야 하는 과업은 무엇이 있는가?
    • 너무 깊은 대답은 어렵고, 코틀린 전환 과정중에 아까 얘기 했던 UI/UX 개선이 있다.
    • (전시파트의 꽃은 검색엔진 아냐? ㅠ)

마지막 할말?

면접을 준비하면서 제 자신에 대해서 알게 됬고, 해당 회사가 어떤 회산지도 알게 되서 좋은 시간이였다. 감사하다.

728x90
반응형

'커리어 디버깅' 카테고리의 다른 글

2023년 회고  (0) 2023.12.30
[회고] 2023년 상반기  (0) 2023.07.02
[면접 회고] 20230420 EI사 1차  (0) 2023.04.20
[면접 회고] W사  (0) 2023.02.27
[면접 회고] C사  (0) 2023.02.27

대기업 계열사 ecommerce 전문 부문 상품/전시 파트 1차 면접, 비대면 화상면접으로 진행 됨.

자기소개

경력기술서 4줄 요약 + 강점으로 소개

이직이 잦았는데 각 회사별 이직 사유를 말해달라

  1. 공간정보 기반 공공기관 SI 사업을 했는데, 매출의 2/3에 해당하는 프로젝트 수주가 실패하고, 회사가 기운이 약해지는 것을 느끼고 이직했다.
  2. 공공기관 si 에이젼시였는데, 기술 성장에 큰 도움이 없는 것이 느껴져서 이직을 결심했다.
  3. 입사 당시 예정된 프로젝트가 있었는데 엎어졌다. 시니어급이 줄 퇴사했고, 그 후로 기존 프로젝트의 개선에 힘썼지만, 시니어급의 부재가 좀 컸다. 그래서 이직을 결심했다.

di ioc에 대해서 설명해보라

DI는 의존성 주입으로, 객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입하는 방식이다. 결합도가 낮아지고 유연성이 높아진다.

(대답 못 하고 한참 버벅임) IoC는 긴장해서 기억이 안 난다.
(면접 망했음을 느낌)

이력서에 jvm 튜닝이 있던데, 어떻게 했나?

하남시청에서 운영하는 운동장 예약 시스템에서 예약 오픈일마다 서버가 뻗었다. 로그를 확인하니 JVM 문제인 것으로 확인되어 튜닝했고, 서버 메모리가 32기가나 되는데, jvm에서 1기가밖에 사용하지 않길래 4기가까지 확장했다.
heap 영역과 stack 영역, 새로운 객체 영역, 오래된 객체 영역에 대한 값을 테스트 해가면서 튜닝했다.

  • 그럼, 메모리를 많이 주게 되는데 GC가 오래 걸리거나 GC 하면서 뻗지 않는가?

깊은 GC 얕은 GC 설정이 있었던 걸로 기억나는데 정확하게 기억이 안 난다.
(2차로 망했음을 느낌)

Redis 어떻게 사용했는가?

WAS를 2대를 수기 배포를 하는 시스템이었고, 배포할 때 번갈아 가면서 배포하면 한쪽에서 세션이 끊기는 문제가 있었다.
WAS를 spring 설정에서 session clustering을 Redis에 연결해서 해결했다.

jsp는 잘 다루는가?

jsp는 스프링이랑 같이 사용했기 때문에 못 다룰 수가 없다고 생각하다.
(설마 model 1에 절차지향적인가?)

template engine에 대해서 아는가? FE는 좀 잘 다루는지?

이전에 근무한 회사에서 FreeMarker를 사용했고, JS template인 handlebar.js나 jquery template를 사용했다. 첫 회사에서 지도를 SPA로 개발했기 때문에 많이 다뤄봤다.

\pagebreak

infra, be, fe 어느 분야를 더 선호 하는가?

일단 spring be가 더 자신 있다.
그렇지만, 첫 회사에서 JS 많이 다뤄서 FE에도 관심이 있고. 인프라도 흥미가 있다.
어쨌든 spring BE를 더 선호한다.

이력서에 있는 대용량 데이터 처리 고도화에 대해 설명해달라

하루에 배송 추적하는 송장이 2,700건 정도 됐던 걸로 기억하는데, 2,700 건을 리스트에 담아서 한 건씩 건건히 API 조회를 하는 방식이었다.
정보를 제공해 주는 API를 개선할 수 없으니 우리 쪽 프로세스를 개선했다.
대상을 100건씩 쪼개서 spring async annotation으로 비동기 처리를 했다. 그리고 DB insert/update 할 때도 시간 소모가 많기 때문에, 한 트랜잭션에서 100건 쿼리 수행하고 트랜잭션을 닫는 처리를 했다.
MyBatis execute batch 옵션을 사용했는데, 해당 옵션을 사용하면 select 쿼리 결과를 재사용하는 특성이 있어서 select와 insert/update 하는 로직도 분리했다.

  • 혼자 처리한 업무인가?

그렇다.

현재 팀 구성이 어떻게 되는가?, 업무 중 협업은 어떻게 하는가?

총 4명으로 차장급 세 명과 일한다.
업무 이슈를 각자 나눠서 처리하는 중이라 협업할 기회가 없다.
그래도 물어보면 잘 대답해 주신다. 그치만 보통은 구글을 많이 참고하고, 최근에는 chatGPT도 활용한다.
개발 협업은 적은데, 마케팅이나 운영 MD, 기획팀과의 협업은 많이 하는 편이다. 주도적으로 커뮤니케이션을 해서 그들의 매출을 증가시키는 데 도움을 주고 있다.

조회쿼리 성능 개선 어떻게 할 것인가?

최근에 진행 중인 이슈인데, 지금 근무 중인 회사를 기준으로 얘기하자면 oracle DB를 사용 중이다. CPU 사용률이 높아서 Metarial view를 사용해서 조회 성능을 개선하는 중이다.

  • 운영자가 실시간 조회 성능 개선을 요구한다면 어떻게 할 것인가?

(한참 고민함)
현재 재직 중인 회사 쿼리를 기준으로 얘기를 하면, select 절에 있는 함수를 paging 처리 후로 다 빼고, inline sub query나 scalar sub query를 join으로 변경하는 방식으로 진행한다.

(원하는 답이 아닌 것도 알고, 정석적인 방법도 알지만 긴장해서 제대로 답 못함. 3차로 망했다)

\pagebreak

질문 할 것 있는가?

  • 사내 개발정보 공유하는 곳이 있다고 들었는데 얼마나 활성화?
    • 최근에 오픈을 했고, 팀별로 기술 자랑하는 분위기.
      우리는 신생팀이라 아직 발표는 안 했는데, 팀마다 월별로 발표도 한다.
  • git 사용한다고 들었는데, github, gitlab 어떤 것을 사용?
    • gitlab ent. 사용 중.
  • 들어갈 팀에 개발자가 몇 명이나 있고, 연령대 분포는 어떻게 되는지?
    • 개발자가 몇 명인지는 대답 못 듣고, 내가 중간쯤 된다고만 기억 남.
    • 아마 직원이 많아서 몇명이나 되는지 모르거나, 사람이 없어서 숨길 수도 있다고 생각됨.
  • 이전에 해결한 이슈나 앞으로 해결해 나가야 하는 이슈에 대해 알고싶다.
    • 상품, 전시는 검색이 꽃인데, 그쪽 관련해서 당장은 서베이 중이다. 곧 관련 이슈를 진행 할 예정이다.
      (조회 쿼리 대답 못한게 정말 ㅈ됨을 느낌)
  • 기술적인 의사 결정이 필요할 때 결정을 내리는 주체가 개인인지, 팀원 전체인지 알고 싶다.
    • 난감해함 (뭔가 잘못된 질문인가 싶음)
    • 당연히 혼자 결정하진 않는다. 탑다운도 아니고 바텀업도 아니다. 회의를 통해서 결정한다.

https://youtube.com/shorts/8WJOxkySoVc?feature=share

 
 
 
728x90
반응형

'커리어 디버깅' 카테고리의 다른 글

[회고] 2023년 상반기  (0) 2023.07.02
[면접 회고] 20230530 COY 면접 회고  (0) 2023.05.30
[면접 회고] W사  (0) 2023.02.27
[면접 회고] C사  (0) 2023.02.27
2022년 회고 및 2023년 목표 설정  (0) 2023.01.03

특정 기간 중, CPU 사용율이 높은 쿼리를 조회한다.
CPU 점유율이 높은 쿼리를 찾아서 튜닝하기 위해서 사용한다. 기본적으로 oracle에서 제공하는 view를 활용한다.

사용 테이블

  • DBA_HIST_SQLSTAT : SQL Historical Statixtics Information
    DB에서 실행된 SQL에 대한 성능 통계치 view
  • DBA_HIST_SNAPSHOT : SnapShot Information
    워크로드 저장소의 스냅샷에 대한 정보 view
  • DBA_HIST_SQLTEXT : SQL Text
    워크로드 저장소에 캡쳐된 공유 SQL 커서에 속한 SQL 문의 텍스트 표시.
    이 view는 주로 V$SQL view와 함께 사용됨.

최종 쿼리

  WITH REF_DATE AS (
      /* 대상 기간 지정 */
      SELECT
          TO_DATE('20220401' || ' 000000', 'YYYYMMDD HH24MISS') AS BGN_DE
          , TO_DATE('20230420' || ' 235959', 'YYYYMMDD HH24MISS') AS END_DE
      FROM DUAL
  )
  SELECT
      X.SQL_ID
      , X.CPU_TIME
      , X.EXECUTIONS_DELTA
      , X.CPU_TIME_PER_EXECUTIONS
      , DBMS_LOB.SUBSTR(SUBSTR(D.SQL_TEXT, 1, 200)) AS SQL_TEXT
      , D.SQL_TEXT AS SQL_FULLTEXT
  FROM (
          SELECT
              SQL.DBID
              , SQL.SQL_ID
              , SUM(SQL.CPU_TIME_DELTA) / 1000000 AS CPU_TIME
              , SUM(SQL.EXECUTIONS_DELTA) AS EXECUTIONS_DELTA
              , ROUND((SUM(SQL.CPU_TIME_DELTA) / 1000000) / DECODE(SUM(SQL.EXECUTIONS_DELTA), 0, 1, SUM(SQL.EXECUTIONS_DELTA)) / DECODE(SQL.PX_SERVERS_EXECS_DELTA, 0, 1, SQL.PX_SERVERS_EXECS_DELTA)) AS CPU_TIME_PER_EXECUTIONS
          FROM DBA_HIST_SQLSTAT SQL
              , (
                  SELECT
                      MIN(SNAP_ID) AS START_SNAP_ID
                      , MAX(SNAP_ID) AS END_SNAP_ID
                      , MIN(BEGIN_INTERVAL_TIME) AS BEGIN_INTERVAL_TIME
                      , MAX(END_INTERVAL_TIME) AS END_INTERVAL_TIME
                  FROM DBA_HIST_SNAPSHOT, REF_DATE
                  WHERE BEGIN_INTERVAL_TIME BETWEEN REF_DATE.BGN_DE
                                              AND REF_DATE.END_DE
              ) SNAP
          WHERE SQL.SNAP_ID BETWEEN SNAP.START_SNAP_ID AND SNAP.END_SNAP_ID
          GROUP BY SQL.DBID, SQL.SQL_ID, SQL.PX_SERVERS_EXECS_DELTA
          HAVING SUM(SQL.EXECUTIONS_DELTA) >= 0
          ORDER BY CPU_TIME_PER_EXECUTIONS DESC
  ) X
  INNER JOIN DBA_HIST_SQLTEXT D
      ON D.SQL_ID = X.SQL_ID
      AND D.DBID = X.DBID
  WHERE ROWNUM <= 40
  ;

분리

  SELECT
      SQL.DBID
      , SUM(SQL.CPU_TIME_DELTA) / 1000000 AS CPU_TIME
      , SUM(SQL.EXECUTIONS_DELTA) AS EXECUTIONS_DELTA
      , ROUND((SUM(SQL.CPU_TIME_DELTA) / 1000000) / DECODE(SUM(SQL.EXECUTIONS_DELTA), 0, 1, SUM(SQL.EXECUTIONS_DELTA)) / DECODE(SQL.PX_SERVERS_EXECS_DELTA, 0, 1, SQL.PX_SERVERS_EXECS_DELTA)) AS CPU_TIME_PER_EXECUTIONS
  FROM DBA_HIST_SQLSTAT SQL
      , (
          SELECT
              MIN(SNAP_ID) AS START_SNAP_ID
              , MAX(SNAP_ID) AS END_SNAP_ID
              , MIN(BEGIN_INTERVAL_TIME) AS BEGIN_INTERVAL_TIME
              , MAX(END_INTERVAL_TIME) AS END_INTERVAL_TIME
          FROM DBA_HIST_SNAPSHOT
          WHERE BEGIN_INTERVAL_TIME BETWEEN TO_DATE('20230401' || ' 000000', 'YYYYMMDD HH24MISS')
                                      AND TO_DATE('20230419' || ' 235959', 'YYYYMMDD HH24MISS')
      ) SNAP
  WHERE SQL.SNAP_ID BETWEEN SNAP.START_SNAP_ID AND SNAP.END_SNAP_ID
  GROUP BY SQL.DBID, SQL.SQL_ID, SQL.PX_SERVERS_EXECS_DELTA
  HAVING SUM(SQL.EXECUTIONS_DELTA) >= 0
  ORDER BY CPU_TIME_PER_EXECUTIONS
  ;
  SELECT
      MIN(SNAP_ID) AS START_SNAP_ID
      , MAX(SNAP_ID) AS END_SNAP_ID
      , MIN(BEGIN_INTERVAL_TIME) AS BEGIN_INTERVAL_TIME
      , MAX(END_INTERVAL_TIME) AS END_INTERVAL_TIME
  FROM DBA_HIST_SNAPSHOT
  WHERE BEGIN_INTERVAL_TIME BETWEEN TO_DATE('20230401' || ' 000000', 'YYYYMMDD HH24MISS')
                              AND TO_DATE('20230419' || ' 235959', 'YYYYMMDD HH24MISS')
  ;
728x90
반응형
 

Head First Design Patterns는 객체지향 프로그래밍과 디자인 패턴에 대한 입문서다. 이 책 또한 다른 전공 서적과 다르게 쉽게 읽히는 책이다. 그림과 예제 코드를 통해 설명되며, 이러한 방식으로 개념을 쉽게 이해할 수 있었다.  
  
"변하지 않는 사실은 계속 변화한다"라는 문장을 책에서 강조하는데, 현업에서 개발하다 보면 기획내용이나 사용자의 요구사항은 계속 변한다. 어제까지는 분명 A를 얘기했는데, 오늘 저녁에 갑자기 B나 C 또는 H로 요구사항이 바뀔 수 있다. 열심히 다 만들었더라도 요구사항이 추가되거나 변하면 새로운 것을 다시 만들어야 하는 상황이 흔한 것이다. 사실 요구사항의 변화는 SW의 본질이라고 할 수 있다. 그렇기에 변경에 용이한 아키텍쳐를 설계하고 개발하는 것이 개발자의 중요 역량이라고 생각한다. 그게 안 되면 IT로 밥 벌어먹으면 안 된다는 생각. 어쨌거나, 변경에 용이한 코드를 작성하기 위해서는 여러 가지 디자인 패턴이 있고, 예제를 패턴을 적용해 개선해 나가는 예시로 설명해준다.

책에서는 디자인 패턴의 개념과 각 패턴이 어떤 상황에서 사용되는지에 대해 설명한다. 디자인 패턴은 각각 객체 생성, 객체 구조, 인터페이스, 행위 등의 다양한 측면에서 소프트웨어 디자인을 개선하기 위한 것이다. 또한, 이 책은 디자인 패턴을 적용하는 과정에서 발생하는 문제와 해결 방법에 대해 다루며, 이를 통해 디자인 패턴을 적용하는 방법을 익힐 수 있도록 돕는다. 디자인 패턴은 사실 무궁무진하게 많다, java, c, javascript 언어에 따라서도 다른 패턴이 나온다. 이 책에서는 java 계열 실무에서 자주 사용하는 디자인 패턴을 집중해서 다뤄주고 있다. 책은 650여 페이지로 굉장히 두꺼운데, 그림이 대다수를 차지하고 있고, 독자의 가독성을 고려하여 쓴 책이라 쉽게 읽힌다. (글을 잘 쓰는 사람이 개발도 잘한다는 말을 반증하는 것 같다.)

나는 객체 지향을 조금 더 잘 활용해보고 싶은 개발자로 추상화, 캡슐화, 다형성, 상속을 어떻게 하면 더 잘 적용할 수 있을지를 이 책을 통해 심도있게 고민해보았다. 흔히들 코드 재사용을 막기 위해 상속을 사용하는데, 생각지 못한 상황에 반복된 코드를 짤 수밖에 없었던 적이 많다. 그런 상황에 대한 해법들도 이 책을 통해 얻었다.
Head First Design Patterns는 객체지향 프로그래밍과 디자인 패턴에 대한 이해를 쉽게 접근할 수 있도록 돕는다. 이 책으로 습득한 디자인 패턴을 사용하면 더 나은 소프트웨어 디자인을 구현할 수 있을 것으로 기대되고, 이는 개발자로서의 역량 향상에 큰 도움이 될 것 같다.

728x90
반응형

채수원 님의 TDD(Test Driven Development) 개발 실천법과 도구는 소프트웨어 개발 방법론 중 하나인 TDD를 구체적으로 실천하는 방법과 그 과정에서 사용되는 도구들에 대한 내용을 다룬 책이다. TDD 대한 이해를 높이고 실제 개발에 적용하는 방법을 설명한다. TDD(Test Driven Development)는 소프트웨어 개발 방법론 중 하나로, 테스트 케이스를 먼저 작성하고 이를 통과하는 코드를 작성하는 것을 중심으로 개발하는 방법

책에서는 TDD의 개념과 원칙을 소개하며 테스트 케이스(TC) 작성, 코드 작성, 리팩토링 등의 단계별로 구체적인 예시를 보여준다. 특히 TC 작성과 이를 통한 코드 작성의 중요성에 대해 강조하며 TDD의 장점과 효과를 다양한 관점으로 보여준다. 책에서는 TDD를 실천하는 데 필요한 기본적인 원칙과 방법론을 설명하면서, 이를 구현하는 과정에서 발생하는 이슈와 해결 방법 등에 대해서도 다룬다. TDD를 적용하는 데에 있어 좋은 습관과 나쁜 습관을 비교하며 TDD를 대하는 개발자의 태도화 실천할 것들을 제시한다.

TDD를 실천하는 과정은 다음과 같다.

  • 테스트 케이스 작성 :
    먼저 개발하려는 기능에 대한 테스트 케이스를 작성. 작성한 테스트 케이스는 해당 기능이 정확히 동작하는지 검증하기 위한 목적으로 작성한다.
  • 테스트 실행 :
    작성한 테스트 케이스를 실행하여 해당 기능이 제대로 구현되어 있는지 확인.
    이때, 테스트 케이스가 실패하면 해당 기능의 구현이 제대로 이루어지지 않았다는 것을 의미.
  • 코드 작성 :
    테스트 케이스를 통과하기 위한 코드를 작성.
    이때, 테스트 케이스가 통과하도록 코드를 작성하는 것이 중요함.
  • 코드 리팩토링 :
    작성한 코드를 리팩토링하여 더 효율적이고 가독성이 좋은 코드로 개선.

이런 과정을 반복하며 개발하는 것이 TDD의 핵심이다. 이 과정에서 개발자는 작성한 코드가 정상적으로 동작하는지 항상 확인할 수 있으며, 버그를 빠르게 발견하고 수정할 수 있다고. 또한, 작성한 코드를 리팩토링하면서 코드의 유지 보수성과 확장성을 높일 수 있다고 한다.

TDD를 실천하는 개발자들은 코드 품질과 안정성을 높일 수 있으며, 개발 프로세스를 보다 효율적으로 관리할 수 있다고 한다. 또한, TDD는 기능 구현 외에도 코드의 문서화와 같은 부가적인 기능을 제공할 수 있어, 소프트웨어 개발의 생산성을 높일 수 있다고 한다.


TDD를 처음 접하는 초심자에게 유용한 정보이지만 초판 이후 어른들의 사정으로 재판이 나오지 않았고, 옛날 기술이나 라이브러리에 대한 설명이 많다. TDD를 업무에서 실 사용해보지 않은 내 입장에서는 일단 겪어봐야 할 것 같다. SW 품질을 높이는데 철학이 없는 조직에서는 TDD를 적용하기가 쉽지 않아서, 어쩌면 뜬구름 잡는 소리로 보이는 것도 있을 것 같다. 그치만 나는 내 일을 하면서 SW 품질을 높이는 데 많은 목마름이 있기에 꼭 TDD를 경험해 봐야 할 것 같다. 일단 나는 사이드 프로젝트에서 TDD를 사용해야겠다.

 
728x90
반응형

'개발도서' 카테고리의 다른 글

[독후감] 헤드퍼스트 디자인 패턴  (0) 2023.04.14
[독후감] 클린 코드  (1) 2023.03.25
[독후감] 읽기 좋은 코드가 좋은 코드다  (0) 2023.03.06
[독후감] 토비의 스프링 3.1 Vol.1  (0) 2023.03.06
필독도서  (0) 2022.07.13

GATHER PLAN STATISTICS

오라클 DB의 예상 실행 계획만으로는 성능 개선에 어려움을 겪을 때가 있음. 실제 실행 계획을 보고 문제점을 진단할 수 있어야 함.
gather_plan_statistics 힌트는 Oracle SQL에서 SQL 문의 실제 실행 통계를 수집할 수 있는 기능.
이 힌드를 사용하면 Oracle DB는 SQL 실행 계획의 각 단계에서 처리된 행 수, 사용된 메모리 양 및 각 단계에서 사용된 시간과 같은 자세한 통계를 수집함.

GATHER_PLAN_STATISTICS으로 수집 가능한 정보

  • 각 단계에서 수행되는 레코드 수, 시간 및 I/O 통계
  • 각 단계에서 사용된 실행 계획
  • SQL 문의 최적 실행 계획과 실제 실행 계획의 차이를 나타내는 비교 정보

GATHER PLAN STATISTICS 사용 법

SQL 문에 다음과 같이 간단히 추가하면 됨.

  SELECT /*+ gather_plan_statistics */ column1, column2, ...
  FROM TABLE
  WHERE ...

DBMS_XPLAN.DISPLAY_CURSOR

성능 문제를 진단하고 SQL 문을 최적화하는데 유용함.
/*+ gather_plan_statistics */ SQL을 실행한 후, DBMS_XPLAN.DISPLAY_CURSOR 함수를 사용하여 실행 계획과 관련된 통계를 볼 수 있음.
DISPLAY_CURSOR 함수는 실행 계획과 통계의 자세한 보고서를 반환하므로 SQL 문의 성능을 분석하고 최적화할 부분을 식별하는 데 사용할 수 있음.

  • 최적 실행 계획과 실제 실행 계획의 비교
  • 각 실행 계획 단계에서 수행된 레코드 수, 시간 및 I/O 통계
  • 각 실행 계획 단계에서 사용된 인덱스 및 조인 방법 등의 정보

PREDICATE INFORMATION 섹션에서는 WHERE절과 JOIN 조건에 대한 추가 정보도 제공함

  SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT => 'ALLSTATS LAST'));
  SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(null, null, 'ALLSTATS LAST'));

DISPLAY_CURSOR 매개 변수

  • SQL_ID : 실행 계획과 통계를 검색하려는 SQL 문의 SQL_ID
  • CURSOR_CHILD_NO : [OPTIONAL] 실행 계획을 검색하려는 SQL 문의 부모 커서 번호, 생략시 첫 번째 커서(부모 커서)가 대상이 됨
  • FORMAT : 출력 형식 지원, 여러가지 출력 옵션이 있음.
    FORMAT=>'ALLSTATS LAST'를 사용하면 모든 실행계획 정보와 통계를 검색함.

SQL_ID 찾기

SQL_ID를 가져오기 위해 현재 DB에 접속한 SESSION에서 실행한 SQL 문의 히스토리를 검색하여 정보를 가져옴.

  --------------------------------------------------------------------------------
  -- SQL_ID, CHILD_NUMBER 추출
  --------------------------------------------------------------------------------
  SELECT
    SA.SQL_ID,
    S.CHILD_NUMBER,
    SA.SQL_TEXT,
    SA.MODULE,
    SA.LAST_LOAD_TIME,
    SA.LAST_ACTIVE_TIME,
    SA.PLAN_HASH_VALUE,
    SA.OPTIMIZER_COST,
    SA.FETCHES,
    SA.EXECUTIONS,
    SA.cpu_time,
    SA.ELAPSED_TIME,
    S.DISK_READS,
    S.PARSE_CALLS,
    S.BUFFER_GETS,
    S.ROWS_PROCESSED,
    SA.PARSING_USER_ID,
    SA.PARSING_SCHEMA_ID,
    SA.PARSING_SCHEMA_NAME
  FROM V$SQLAREA SA
    /* V$SQLAREA : 공유 SQL 영역 */
    INNER JOIN V$SESSION SS
      /* V$SESSION : 현재 세션에 대한 정보 */
      ON SA.PARSING_USER_ID = SS.USER#
      AND SA.PARSING_SCHEMA_ID = SS.SCHEMA#
    INNER JOIN V$SQL S
      /* V$SQL : 공유 SQL 영역 내 쿼리에 대한 정보 */
      ON SA.SQL_ID = S.SQL_ID
  WHERE SS.AUDSID = USERENV('SESSIONID')
    /* 현재 세션과 같은 SESSION ID */
    AND SS.SID = USERENV('SID')
    /* 특정 스키마 */
    AND SA.PARSING_SCHEMA_NAME = 'IDLOOK'
    /* 약 15분 이내에 실행한 쿼리만 조회 */ 
    AND SA.LAST_ACTIVE_TIME >= SYSDATE - 0.01
    /* 제외 */
    AND sa.PARSING_SCHEMA_NAME NOT IN ('SYS', 'SYSTEM')
    AND sa.SQL_TEXT NOT LIKE '%DBMS%'
    AND sa.SQL_TEXT NOT LIKE '%V$%'
    /* 실행 모듈이 운영 또는 타 시스템에서 실행한 쿼리라면 제외 */
    AND SA.MODULE NOT IN ('DBMS_SCHEDULER', 'JDBC Thin Client', 'w3wp.exe')
    /* 특정 문자열 제외*/
    AND NOT REGEXP_LIKE(UPPER(SA.SQL_TEXT), 'V\$SQL|PLAN_TABLE|DBMS_XPLAN|EXTRACTVALUE\(|XMLSEQUENCE\(|CURRENT_SCHEMA|DBA_|DBMS_UTILITY|CONSTRAINT')
  ORDER BY SA.LAST_ACTIVE_TIME DESC, SA.SQL_ID, S.CHILD_NUMBER
;

권한 문제

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT=>'ALLSTATS LAST'));
쿼리 실행 시 권한 부족한 경우 DBA 계정으로 로그인하여 권한 부여 작업을 수행하거나 DBA 권한이 있는 사용자에게 권한을 부여해야 함.
DBMS_XPLAN.DISPLAY_CURSOR 함수를 사용하려면 V$SESSION, V$SQL_SESSION, V$SQL(OPTIONAL), V$SQL_PLAN_STATISTICS_ALL 권한이 필요. (해당 권한이 없는 경우 ORA-01031: insufficient privileges 오류가 발생)

작동 조건

  • PLAN_STATISTICS 정보는 다음 조건 중 하나를 만족해야 함.
    • STATISTICS_LEVEL PARAMETER 값을 ALL로 변경 한 경우
      ALTER SESSION SET STATISTICS_LEVEL = ALL;
    • _ROWSOURCE_EXECUTION_STATISTICS PARAMETER 값을 TRUE로 변경한 경우
    • GATHER_PLAN_STATISTICS HINT를 사용

조회 결과 분석

  • Id, Operation, Name :
    흔히 봐온 플랜 정보, 자원에 대한 접근 순서와 방법을 나타냄. 접근 순서를 변경할 수 있는 힌트 절은 ORDERED, LEADING이 있음.
    또한 접근 방법을 변경할 수 있는 힌트절은 USE_NL, USE_HASH, USE_MERGE가 있음.
  • Starts :
    오퍼레이션을 수행한 횟수를 의미한다. Starts * E-Rows 의 값이 A-Rows 값과 비슷하다면, 통계정보의 예측 Row 수와 실제 실행 결과에 따른 실제 Row 수가 유사하다고 함.
    만약 값에 큰 차이가 있다면 통계정보가 실제의 정보를 제대로 반영하지 못했다고 봐야 한다고 함.
    이로 인해 오라클의 Optimizer가 잘못된 실행 계획을 수립할 수도 있음을 염두에 둬야 함.
  • E-Rows (Estimated Rows) :
    통계정보에 근거한 예측 Row 수를 의미. 통계정보를 갱신할수록 값이 매번 다를 수 있으며, 대부분의 DB 운영에서는 통계정보를 수시로 갱신하지 않으므로 해당 값에 큰 의미를 둘 필요는 없음.
    하지만 E-Rows 값과 A-Rows 값이 현격하게 차이가 있다면 오라클이 잘못된 실행 계획을 세울 수도 있음을 인지해야 하며 통계정보 생성을 검토해 보아야 함.
  • A-Rows (Actual Rows) :
    쿼리 실행 결과에 따른 실제 Row 수를 의미.
    A-Rows 에서 중요한 여러 정보를 추정 할 수 있음.
  • A-Time (Actual Elapsed Time) :
    쿼리 실행 결과에 따른 실제 수행 시간을 의미.
    실행 시점의 여러 상황이 늘 가변적이고 또한 메모리에 올라온 Block의 수에 따라서 수행 시간이 달라지므로 해당 값에 큰 의미를 두지 않는게 좋음.
  • Buffers (Logical Reads) :
    논리적인 Get Block 수를 의미.
    해당 값은 오라클 옵티마이저가 일한 총량을 의미하므로, 튜닝을 진행할 때 중요한 요소로 보임.

위의 헤더에서 튜닝 시 가장 중요하게 활용되는 부분은 Buffers, A-Rows.
Buffers 값을 통해서 Get Block의 총량을 알 수 있고, A-Rows를 통해 플랜 단계별로 실제 Row 수를 알 수 있음.

DBMS_XPLAN.DISPLAY_CURSOR 조회 결과

  SELECT * FROM TABLE (DBMS_XPLAN.DISPLAY_CURSOR('3YTNQSYC1PXJ8', NULL, 'ADVANCED ALLSTATS ALL -PROJECTION +ROWS +BYTES +PREDICATE'));

 

 

 


 
728x90
반응형

SELECT 시에는 꼭 필요한 column 만 불러오기

많은 필드를 불러 올수록 DB는 더 많은 로드를 부담하게 되기 때문에 꼭 필요한 열만 물러오도록 한다.

  -- WORST
  SELECT * FROM TABLE;
  -- BETTER
  SELECT COLUMN1, COLUMN2, COLUMN4, COLUMN8 FROM TABLE;

WHERE절에서 연산을 걸지 않는다.

연산이 들어가게 되면 TABLE FULL SCAN을 하면서 모든 값을 탐색, 계산 한 뒤 조건 충족 여부를 판단하기 때문에 좋지 않다.

  -- WORST
  SELECT COLUMN1, COLUMN4
  FROM TABLE
  WHERE FLOOR(COLUMN4) = 2;
  -- BETTER
  SELECT COLUMN1, COLUMN4
  FROM TABLE
  WHERE COLUMN4 BETWEEN 4 AND 5;

LIKE 조회시 와일드카드 % 는 가급적 뒤에만 붙이자

COLUMN6 LIKE %DF 는 TABLE FULL SCAN을 유발한다.
COLUMN5 IN ('ASDF', 'ERDF'), COLUMN5 = 'ASDF' OR COLUMN5 = 'ERDF' 같은 형태가 낫다.

SELECT DISTINCT, UNION ALL 과 같이 중복을 제거하는 연산은 자제한다

중복을 제거하는 연산은 시간이 많이 걸린다. 불가피하게 사용해야 할 경우 EXISTS, GROUP BY를 활용하는게 낫다.
DISTINCT는 원하는 컬럼에 대해서 중복을 제거하는 것이 아니라 SELECT 해온 모든 ROW에서 중복을 제거하므로 속도가 느려진다.

같은 내용의 조건이라면 GROUP BY 연산의 HAVING 보다는 WHERE 절을 사용하는 것이 좋다

쿼리 실행 순서에서 WHERE 절이 HAVING 절 보다 먼저 실행된다. 따라서 WHERE 절로 미리 데이터를 작게 만들면 GROUP BY 절에서 다뤄야 하는 데이터 크기가 작기 때문에 효율적인 연산이 가능하다.

VIEW VS MVIEW (MATERIALIZED VIEW)

뷰는 질의 할때마다 해당 쿼리를 재 실행하는 것과 같음. 속도가 느림. 다만 데이터는 LIVE 함.
MVIEW는 세팅을 어떻게 하느냐에 따라 질의 할 쿼리를 재사용하여 가져옴. 스냅샷 처럼 이전에 만들어 놓은 엠뷰 테이블에서 데이터를 가져옴.
비용이 많이 들어가고 데이터가 고정적인 경우 엠뷰를 만들어서 사용하는게 좋음. 인덱스도 생성 가능.
리프레시 타임이 많이 드는 경우 데이터가 LIVE하지 않은 문제가 있음.

CHAR VS VARCHAR

4byte 이하는 CHAR, 그 이상은 VARCHAR가 나음.

  • CHAR : 고정 길이 문자열
  • VARCHAR : 가변 길이 문자열

BLOB VS TEXT

많은 양의 데이터 저장, 디폴트 값 지정 안됨, 문자열 뒷부분 공백 제거 안되는 공통점이 있음.
차이점으로 BLOB은 대소문자를 구분, TEXT는 구분 안함.

PRIMARY KEY, UNIQUE KEY

PK와 Unique Key는 자동으로 인덱스가 생성됨.

쿼리 실행 순서

SUB-QUERY -> MAIN-QUERY

INNER JOIN시 테이블 배치 순서

3개 이상의 테이블을 INNER JOIN을 할 때는 크기가 가장 큰 테이블을 FROM 절에 배치하고 INNER JOIN 절에 남은 테이블을 작은 순서대로 배치하는 것이 좋다.
테이블 한두개차이는 상관 없지만 많은 테이블을 JOIN 할 경우 JOIN의 경우의 수가 생기고 OPTIMIZER가 PLANNING을 하면서 비용이 증가된다.

실행 계획 type이 index라면 한 번 더 살펴보기

EXPLAIN으로 실행 계획을 확인 했을 때 type이 index인 부분이 있다. where 절 조건문에서 사용하는 테이블의 컬럼에 인덱스가 제대로 걸려있지 않기 때문에 TABLE FULL SCAN을 한다.
해당 테이블에 index를 걸고 다시 실행 계획을 INDEX RANGE SCAN으로 변경되었고 실제 실행 속도가 1/4로 줄었다. 아래는 type의 종류로 성능이 낮은 것 부터 높은 순으로 정렬했다.

  • ALL : 테이블을 처음부터 끝까지 탐색하여 데이터를 찾음 (TABLE FULL SCAN)
  • INDEX : 인덱스를 처음부터 끝까지 탐색하여 데이터를 찾는 방식 (INDEX FULL SCAN)
  • RANGE : 특정 범위 내에서 인덱스를 사용하여 원하는 데이터를 추출, 데이터가 방대하지 않다면 준수함. (INDEX RANGE SCAN)
  • REF : 조인 할 때 Primary Key 혹은 Unique Key 가 아닌 Key로 매칭 한 경우
  • EQ_REF : 조인 할 때 Primary Key로 매칭
728x90
반응형

이론 적인 것은 잘 정리된 글이 많기 때문에 생략.
java로 int 형 stack과 Object형 stack을 구현한 코드.


int 형을 저장하는 스택

public class IntStack {
    // 스택용 배열
    private int[] stk;
    // 스택 용량
    private int capacity;
    // 스택 포인터
    private int ptr;

    // 실행 시 예외 : 스택이 비어있는 경우
    public class EmptyIntStackException extends RuntimeException {
        public EmptyIntStackException(){}
    }

    // 실행 시 예외 : 스택이 가득 찬 경우
    public class OverflowIntStackException extends RuntimeException {
        public OverflowIntStackException(){}
    }

    // 생성자
    public IntStack(int maxLen) {
        ptr = 0;
        capacity = maxLen;
        try {
            // 스택 본채용 배열 생성
            stk = new int[capacity];
        } catch (OutOfMemoryError e) {
            // 생성할 수 없음
            capacity = 0;
        }
    }

    // 스택에 X를 푸시
    public int push(int x) throws OverflowIntStackException {
        if (ptr >= capacity)
            // 스택이 가득 찬 경우
            throw new OverflowIntStackException();

        return stk[ptr++] = x;
    }

    // 스택에서 데이터를 팝 (꼭대기에 있는 데이터를 꺼냄)
    public int pop() throws EmptyIntStackException {
        if (ptr <= 0)
            // 스택이 비어 있음
            throw new EmptyIntStackException();

        return stk[--ptr];
    }

    // 스택에서 데이트를 피크 (꼭대기에 있는 데이터 조회)
    public int peek() throws EmptyIntStackException {
        if (ptr <= 0)
            // 스택이 비어 있음
            throw new EmptyIntStackException();

        return stk[ptr - 1];
    }

    // 스택을 비움
    public void clear() {
        // 모든 작업이 ptr 값으로 이루어 지므로 배열의 요소를 변경할 필요가 없음.
        ptr = 0;
    }

    // 스택에서 X 찾아 인덱스 반환, 없다면 -1 반환
    public int indexOf(int x) {
        // 뒤(꼭대기)에서 부터 선형 탐색
        for (int i = ptr - 1; i >= 0; i--) {
            if (stk[i] == x)
                // 검색 성공
                return i;
        }
        // 검색 실패
        return -1;
    }

    // 스택의 용량 반환
    public int getCapacity() {
        return this.capacity;
    }

    // 스택에 쌓여있는 데이터 개수 반환
    public int size() {
        return ptr;
    }

    // 스택이 비어있는지 확인
    public boolean isEmpty() {
        return ptr <= 0;
    }

    // 스택이 가득 찼는지 확인
    public boolean isFull() {
        return ptr >= capacity;
    }

    public void dump() {
        if (ptr <= 0)
            System.out.println("EMPTY STACK");
        else {
            for (int i = 0; i < ptr; i++)
                System.out.print(stk[i] + " ");

            System.out.println("");
        }
    }
}

Object 형을 저장하는 스택

// 임의의 객체형을 쌓을 수 있는 테네릭 스택 클래서 Stack<E> 작성
public class GenericStack<E> {
    // 스택용 배열
    private E[] stk;
    // 스택 용량
    private int capacity;
    // 스택 포인터
    private int ptr;

    // 실행 시 예외 : 스택이 비어있는 경우
    public static class EmptyGStackException extends RuntimeException {
        public EmptyGStackException(){}
    }

    // 실행 시 예외 : 스택이 가득 찬 경우
    public static class OverflowGStackException extends RuntimeException {
        public OverflowGStackException(){}
    }

    // 생성자
    public GenericStack(int maxLen) {
        ptr = 0;
        capacity = maxLen;
        try {
            // 스택 본채용 배열 생성
            stk = (E[])new Object[capacity];
        } catch (OutOfMemoryError e) {
            // 생성할 수 없음
            capacity = 0;
        }
    }

    // 스택에 X를 푸시
    public E push(E x) throws OverflowGStackException {
        if (ptr >= capacity)
            // 스택이 가득 찬 경우
            throw new OverflowGStackException();

        return stk[ptr++] = x;
    }

    // 스택에서 데이터를 팝 (꼭대기에 있는 데이터를 꺼냄)
    public E pop() throws EmptyGStackException {
        if (ptr <= 0)
            // 스택이 비어 있음
            throw new EmptyGStackException();

        return stk[--ptr];
    }

    // 스택에서 데이트를 피크 (꼭대기에 있는 데이터 조회)
    public E peek() throws EmptyGStackException {
        if (ptr <= 0)
            // 스택이 비어 있음
            throw new EmptyGStackException();

        return stk[ptr - 1];
    }

    // 스택을 비움
    public void clear() {
        // 모든 작업이 ptr 값으로 이루어 지므로 배열의 요소를 변경할 필요가 없음.
        ptr = 0;
    }

    // 스택에서 X 찾아 인덱스 반환, 없다면 -1 반환
    public int indexOf(Object x) {
        // 뒤(꼭대기)에서 부터 선형 탐색
        for (int i = ptr - 1; i >= 0; i--) {
            if (stk[i].equals(x))
                // 검색 성공
                return i;
        }
        // 검색 실패
        return -1;
    }

    // 스택의 용량 반환
    public int getCapacity() {
        return this.capacity;
    }

    // 스택에 쌓여있는 데이터 개수 반환
    public int size() {
        return ptr;
    }

    // 스택이 비어있는지 확인
    public boolean isEmpty() {
        return ptr <= 0;
    }

    // 스택이 가득 찼는지 확인
    public boolean isFull() {
        return ptr >= capacity;
    }

    public void dump() {
        if (ptr <= 0)
            System.out.println("EMPTY STACK");
        else {
            for (int i = 0; i < ptr; i++)
                System.out.print(stk[i] + " ");

            System.out.println("");
        }
    }
}
728x90
반응형

'Computer Science > DataStructure' 카테고리의 다른 글

[DS] Array 배열  (0) 2022.07.17
  • "나쁜 코드는 깨진 유리창처럼 계속 나쁜 코드가 만들어지도록 한다."
  • "나쁜 코드는 기술 부채를 만들어 수정을 더 어렵게 하며 결국 조직의 생산성을 저하시킨다."

이 책을 추천 받은 것은 개발 쪽으로 전향한 지 얼마 안되서였던 것 같다. 인터넷에서 추천받았었는지, 아니면 국비지원 학원이 끝나갈 무렵이었는지 정확히는 기억나지 않는다. 그리고 최근에 내가 잘 따르던 선배의 강력한 추천이 한번 더 있었다.
어쨌든 토비의 스프링과 함께 한 번은 봐야지 하던 책이었고, 이번에 드디어 완독을 했다. 아니 1독을 했다고 표현해야겠다.

읽코 좋코가 이 책 보다 조금 더 쉽게 읽힌달까. 나중에 후배들을 맞이하고, 혹은 팀을 이끌어갈 때쯤 2독 3독을 해야 할 책이라고 생각된다. 충분히 많은 도움이 된 좋은 책이지만, 내 경험이 짧아 이해하기 어려운 부분도 많고 확실히 내 수준보다는 더 높았다. 아마도 TDD 라던지, 객체지향 패러다임을 잘 따르는 코드를 본 적이 없어서일까?
아무튼 프로세스를 만들 때 가져야 할 마음 가짐에 대해서 많이 생각하게 되었다. 의미를 가진 코드, 코드의 문맥, 추상화 수준과 왜 그렇게 해야 하는지의 이유도 잘 설명되었다. 코드가 마치 잘 쓰여진 소설처럼 읽힐 수 있도록 작성한다는 마음가짐으로 코딩을 하게 되었다. 단순하게 동작이 목적이 아닌 쉽게 읽힐 수 있도록 고민을 하다 보니 더 직관적이고 객체지향 구조로 코드를 짤 수 있었던 것 같다.

선배가 이 책을 추천할 때, 레거시 코드에 대해 정말 화를 내고 열변을 토했던 것으로 기억한다. 그리고 이 책을 읽으면서 그가 이 책을 추천하면서 왜 그렇게 화를 냈는지 납득했다. 저자는 나쁜 코드가 나쁜 이유에 대해서 여러 예를 들고 그중 기억에 남는 것은 위의 두 문장이다.

나 역시도 나쁜 코드를 혐오한다. 레거시 코드를 유지보수하면서 소스코드의 히스토리를 보면 왜 이런 코드가 생겼는지 납득할 수 있지만, 그렇다면 처음부터 더 좋은 구조로 작성될 순 없었는가 하는 의문이 들곤 한다. 솔직하게 말해서 나쁜 코드를 보면 화도 나고 작성자를 기둥에 묶어두고 싶다는 생각도 든다.
나를 포함한 많은 개발자들이 이 책을 읽고 객체지향 패러다임과 클린코드 원칙들을 따르는, 개발을 잘하는, 후배들에게 좋은 코드를 남기는 시니어가 될 수 있도록 갈고닦기를... 🙏🏻

 
728x90
반응형

+ Recent posts