채용공고 올리기

김강섭님을 응원해보세요!

지금 만족하고 있어요

미리보기

기본 정보

이름
김강섭
직업
백엔드 개발자
간단 소개

현재 운용중인 사이트 : http://3.34.40.88/ 사용한 스택 : Django (Python, JavaScript, MySQL) / AWS (EC2, RDS) / Nginx(정적), Gunicorn(동적) / Docker Python, Java, Spring boot, Django, Django-Rest-API, Node.js, javascript를 이용해 웹개발, RestAPI를 개발합니다. ERD를 설계하고, MySQL를 사용할 수 있습니다. HTML/CSS로 프론트엔드 개발도 가능합니다. 단국대학교에서 수학과를 전공했으며, 이를 바탕으로 알고리즘 관련 공부를 시작하게 되었습니다. 알고리즘 대회인 '백준 아레나'에도 꾸준히 참가하였으며, 백준 골드4 티어를 달성하였습니다. 이후 부트캠프 멀티캠퍼스 풀스택을 수료하였으며, 커뮤니케이션을 통한 공동개발 경험이 있습니다. 현재 개발능력을 향상 시키기 위해 지속적인 개인 개발 블로그를 운영중에 있습니다.

자기소개

자기소개

DB탐색 [Indexing]

MySQL을 사용한 Spring boot 프로젝트에서 게시글을 검색하는데
JPA을 사용하여 단순 ORM을 날려서 검색을 한다고 가정해보면

List<Posts> postsList = postsRepository.findByTitleContaining(keyword);

이는 쿼리문으로 다음과 같습니다.

SELECT p From Posts p WHERE p.title LIKE %:keyword%

이 쿼리문의 문제점은 2가지입니다.

  1. 정확하게 "특정한 단어"가 포함된 title의 Post만 가져온다는 것

  2. JPA의 특성상 N+1 문제가 생길 수 밖에 없다는 것

그래서 해결 방법으로 indexing, Querydsl을 사용했습니다.
우선 사용하고 있는 DB, MySQL에 title column을 indexing 하였습니다.
이 때, indexing을 하는 방법으로 MySQL에서 기본적으로 제공하는 Indexing인,

FULL-TEXT Indexing (with N-gram parser)이 있습니다.

제목과 같은 특정 단어를 검색했을때 검색이 되도록하려면 FULL-TEXT Indexing을 해야된다 생각하였고,
한글의 특성상 N-gram parser를 꼭 사용해야 검색의 결과가 유의미하다 생각하여 추가하였습니다.

CREATE FULLTEXT INDEX idx_title_fulltext ON Posts(title) WITH PARSER ngram;;

이렇게 만들어진 FULL-TEXT 인덱스를 사용하기 위해서는 Querydsl이 필요합니다. 이를 PostServiceImpl 에 다음과 같이 Querydsl를 사용해 쿼리문을 작성합니다.

@Autowired private JPAQueryFactory queryFactory;
@Override public List<Posts> findByTitleContaining(String keyword) {

    QPosts posts = QPosts.posts;

    BooleanExpression matchExpression =
        Expressions.booleanTemplate( "MATCH({0}) AGAINST ({1} IN BOOLEAN MODE)",
        posts.title, keyword);
        return queryFactory.selectFrom(posts)
                 .leftJoin(post.post_writer, post_writer).fetchJoin()
                 .where(matchExpression)

이제 이렇게 작성된 코드는 JPA의 고질적인 문제인 N+1문제도 해결됩니다.

이후 PostServiceImpl을 PostService에 implements 하여 인터페이스를 불러오고,

그 PostService를 PostController에 불러와 사용하면 됩니다.

다만, 이렇게 만들어진 코드를 hibernate를 사용하여 로그를 확인해봤을때,

쿼리문의 갯수가 기존 N+1에서 1개로 확실하게 줄어들었긴했지만, 출력되는 시간은 비슷했습니다.

이렇게 된 이유로는 2가지가 추정되는데

  1. 작성된 Post의 숫자가 적어서 딱히 시간 단축이 일어날 정도의 효과가 일어나지 않았다.

  2. 가입된 Member의 숫자가 적어서 기존에도 딱히 N+1의 문제가 심각하기 일어나지 않았다.

그럼에도 db에 요청하는 query문의 갯수가 확연하게 줄어들었으며,

장기적인 서비스를 제공할시 이는 DB서버에 부하를 줄여주는 긍정적인 효과를 가져올 것입니다.


DB column 갯수 최적화 [확장성 있는 Table 설계]

plus x 전자 결재 시스템을 구현하는 과제를 하는 과정에서 이를 최적화 할 수 있는 방법이 없을까 고민했습니다.

기존의 column은 다음과 같습니다.

  • 결재자 0, 결재자 0의 승인여부 (재고 중, 반려, 승인), 결재자 0의 결재 시간

  • 결재자 1, 결재자 1의 승인여부 (결재자 1 null, 결재자 0이 아직 결재 안함, 재고 중, 반려, 승인), 결재자 1의 결재 시간

  • 결재자 2, 결재자 2의 승인여부 (결재자 2 null, 결재자 1이 아직 결재 안함, 재고 중, 반려, 승인), 결재자 2의 결재 시간

이 정도로 하나의 row에 많은 정보를 담아야 되는 경우는 처음이였기 때문에,

지나치게 복잡하다 생각하여 Char Field와 Integer Field중에 무엇이 더 나은지 고민하게 되었습니다.

만약에 Char Field로 구성을 하게 된다면 다음과 같은 장점이 있습니다.

  1. 직관적으로 사람이 읽고 이해하기 쉽습니다.

만약 Integer Field로 구성을 하게 된다면 다음과 같은 장점이 있습니다.

  1. 정수형 데이터는 일반적으로 더 적은 저장 공간을 차지합니다.

  2. 정수 비교는 텍스트 비교보다 빠르므로 성능이 더 좋습니다.

  3. 사전에 정의된 숫자 ID와 매핑되므로 데이터가 더 일관되고 무결성이 보장됩니다.

결재자의 유무를 Boolean Field로 나누는 방법도 생각해봤으나, 오히려 복잡도를 가중시킬 것 같아서 제외했습니다.

결과적으로, 다음과 같은 이유로 Integer Field를 유지하게 되었습니다.

    # 결재 승인할 대상 (최대 3명) 0 -> 아예 없어야 함, 1 -> 결재를 해야되는 대상이지만 당장은 아님, 2 -> 재고 중, 3 -> 반려, 4 -> 승인
    reviewer_0 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='review_1st_approval', on_delete=models.PROTECT)
    is_pass_0 = models.IntegerField(default=2)
    is_pass_0_time = models.DateTimeField(auto_now=True)

    reviewer_1 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='review_2nd_approval',on_delete=models.PROTECT,null=True)
    is_pass_1 = models.IntegerField(default=0)
    is_pass_1_time = models.DateTimeField(auto_now=True)

    reviewer_2 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='review_3rd_approval',on_delete=models.PROTECT,null=True)
    is_pass_2 = models.IntegerField(default=0)
    is_pass_2_time = models.DateTimeField(auto_now=True)

    # 최종적으로 마지막까지 승인이 된다면...! 0 -> 아직 아무것도 진행안됨, 1 -> 최종반려, 2 -> 최종승인, 3 -> 이미 진행됨
    final_approval = models.IntegerField(default=0)
    final_approval_time = models.DateTimeField(auto_now=True)
    reject_reason = models.CharField(max_length=500, null=True,blank=True)

댓글, 대댓글 깊이 구현

댓글 밑에 대댓글이 달리면 깊이가 "1" 만큼 더 들어가 대댓글임을 표현할 수 있도록 구현했습니다.

  1. Post가 작성됨

  2. A가 Comment를 작성함

  3. B가 A의 Comment에 reComment를 작성함

  4. 이 때 B의 댓글은 A의 댓글의 '밑에 위치하며 깊이가 "1만큼" 더 들어가야함

이를 위해서 Comment의 column에는 다음과 같은 정보가 필요합니다.

  1. Integer Ordernumber // 몇 번째 순서에 이 댓글이 위치할지

  2. Integer Depth // 이 댓글은 깊이가 몇인지

이를 위해서 다음과 같이 Column을 구성했습니다.

@Entity
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment extends JpaBaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String content;

    ... 생략 ...

    private Integer depth;
    private Long orderNumber;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Comment parent;

    @OneToMany(mappedBy = "parent")
    private List<Comment> replies = new ArrayList();

    public Comment (String content, Post post, Comment parent, Member writer) {
        this.content = content;
        this.post = post;
        this.comment_writer = writer;
        this.parent = parent;
        if (parent == null) {
            this.depth = 0;
            this.orderNumber = post.getCommentCnt();
        } else {
            this.depth = Math.min(parent.getDepth()+1,5) ;
            this.orderNumber = parent.getOrderNumber();
        }
        this.deletedTime = null;
        this.deletedTrue = false;
        this.likes = 0L;
    }
    ... 생략 ...

    public void repliesSet (Comment comment) {
        this.replies.add(comment);
    }
}
  1. 댓글을 작성한다

  2. 부모 댓글이 없다면 Post의 OrderNumber +1 하여 row 저장

  3. 만약 부모 댓글이 존재 한다면, 부모 댓글의 OrderNumber을 그대로 저장하고 부모 댓글의 Depth + 1 하여

    row에 저장됩니다.

이후 댓글을 불러오는 쿼리문은 다음과 같습니다.

QueryResults<Comment> results = queryFactory.selectFrom(qcomment)
                .leftJoin(qcomment.comment_writer,QMember.member)
                .where(qcomment.deletedTrue.eq(false),
                        qcomment.post.eq(post))
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())

                .orderBy(qcomment.orderNumber.asc(),
                        qcomment.id.asc())

                .fetchResults();

        List<Comment> comments = results.getResults();

다음과 같이 orderBy를 설정하면 구현하고 싶었던 댓글, 대댓글을 구현할 수 있고,

댓글의 Pagination도 문제없이 구현할 수 있습니다.

이후 html에서 템플릿 엔진으로 데이터를 가져올때 Depth에 따라 깊이를 설정하면

댓글, 대댓글, 그리고 대댓글의 깊이까지 구현이 가능합니다.

기술 스택

기술 스택

Python, Django, Django-REST-Framework, MySQL, Java, Spring Boot, querydsl, JavaScript, AWS, Docker, JPA

프로젝트

프로젝트명

PlusX_면접과제 (전자 결재시스템)

소속/기관명

1인 개발

프로젝트 기간

2024.06. ~ 2024.06.

프로젝트 내용

면접 과제입니다.
전자 결재 시스템을 구현했습니다.

깃허브 : 비공개
사용기술 : Python3, Django, Css, HTML, javascript, Aws s3
개발기간 : 3일

Accounts 생성, 로그인, 로그아웃 구현

Model
  • 개인정보 - 유저이름, 이메일, 핸드폰번호

  • 기본정보 - 생성시간, 삭제유무

  • 회사 내 정보 - 직책, 부서

Approvals CRUD 구현

Model
  • 기본정보 - 결재 기안자, 결재 내용, 첨부 파일 연동 (S3 연동), 결재 시간, 수정 시간, 적용일, 삭제 유무

  • 결재 승인권자 - 결재자 1, 결재자 2, 결재자 3

  • 결재가 완료됐는지 - 결재자 1 (IntegerField - 2 : 재고중, 3 : 반려, 4 : 승인)

결재자 2 (IntegerField - 0 : Null, 1 : 이전 결재자 아직 재고중, 2 3 4 위와 동일)

결재자 3 (결재자 2와 동일)

  • 결재 시간 - 결재자 1, 2, 3의 결재 시간 자동 기입

  • 최종 승인이 됐는지 - 최종 결재 여부, 최종 결재 시간 (혹은 반려가 됐다면 반려 사유)

Create
  • (S3에 올린 문서, 파일을 보기 쉽도록 metadata의 Content-disposition과 contentType 지정)

-> 새로운 창에 img 또는 pdf 출력

  • 본인과 같거나 높은 직급의 사람에게만 결재가 가능하도록 필터링

  • 1명에게만 기안이 가능하도록 설정

Main Page
  • main page에서 내가 올린 결재와 내가 해야되는 결재 같이 출력

  • pagination 적용

  • 우측에 현재 진행상태 확인 가능 ( 진행대기, 검토 중, 승인 대기, 승인, 반려, 삭제)

  • 본인이 작성한 글에 한해서 삭제해도 확인가능

Detail Page
  • 본인이 작성한 글일시 진행대기로 표기

  • 본인이 결재자일 경우 반려하기 승인하기 표기

  • 각 결재자가 결재를 했는지 여부 확인 가능 + 결재 시 시간 자동 노출

  • 그 어떤 결재자도 결재를 진행하지 않았을 시 삭제가능

  • 반려하기 클릭시 반려 사유 적어서 form post 요청 구현

  • 삭제하기 클릭시 삭제 유무 다시 물어보기

프로젝트명

Spring_board_ver_2

소속/기관명

1인 개발

프로젝트 기간

2024.06. ~ 2024.06.

프로젝트 내용

Java/Spring 개발 능력 향상을 목표로 사이트의 Member, Post CRDU를 전부 진행한 프로젝트입니다.
https://github.com/S05p/spring_board_ver2
사용기술 : Java, Spring, MySQL, JPA, Querydsl
개발기간 : 8일
도전과제 :
Querydsl을 활용해 댓글 대댓글의 깊이 구현

프로젝트명

Node_project

소속/기관명

1인 개발

프로젝트 기간

2024.04. ~ 2024.04.

프로젝트 내용

Node.js 개발 능력 향상을 목표로 사이트의 Account, Article CRDU를 전부 진행한 프로젝트입니다.
https://github.com/S05p/node_project
사용기술 : Javascript, Node.js, MongoDB,Css, HTML
개발기간 : 2일
도전과제 :
AWS S3를 사용해 이미지 업로드 구현

프로젝트명

talecrew_assignment [테일크루 면접 과제]

소속/기관명

개인

프로젝트 기간

2024.03. ~ 2024.03.

프로젝트 내용

면접 과제입니다.
쿠폰 발급 구현을 구현했습니다.
https://github.com/S05p/talecrew_assignment
사용기술 : Python3, Django, Css, HTML, redis
개발기간 : 2일
1.작품별로 쿠폰 받기를 클릭하여 쿠폰을 발급 받을 수 있는 이벤트 페이지를 구현
2. 사용자는 "쿠폰받기" 버튼을 클릭하여 쿠폰을 발급 받을 수 있어야 함
3. 한 사용자는 같은 작품의 쿠폰을 중복하여 발급받을 수 없음
4. 쿠폰이 모두 소진되면 이벤트는 종료처리되고, 추가 발급되지 않아야 함
5. 순간적으로 증가할 수 있는 트래픽에 대응할 수 있도록 구현

프로젝트명

헬스 커뮤니티 게시판 [Health_Life]

소속/기관명

1인 개발

프로젝트 기간

2023.12. ~ 2024.01.

프로젝트 내용

운동인들이 소통할 수 있는 커뮤니티 사이트입니다.
1인 개발 능력 향상을 목표로 사이트의 Account, Article CRDU를 전부 진행한 프로젝트입니다.
https://github.com/S05p/Health-Life
사용기술 : Python3, Django, AWS, Git, Sqlite3, JavaScript, Css, HTML, Django-rest-framework, docker, redis
개발기간 : 5주
도전 과제 :

  1. 토글 버튼을 통해 User의 Hobby를 ‘여러개’ 지정가능 기능 구현
  2. Category Model을 만들어서 게시판 분류 기능 구현
  3. Recomment Forigenkey로 구현'
  4. AWS를 사용하여 내가 만든 프로젝트 배포
  5. 1인 개발 프로젝트 진행으로 전반적인 프로세스 이해
프로젝트명

부트캠프 정보 비교 사이트 [Final Project]

소속/기관명

멀티캠퍼스 (팀 프로젝트)

프로젝트 기간

2023.06. ~ 2023.06.

프로젝트 내용

부트캠프를 비교할 수 있는 커뮤니티 사이트입니다.
처음으로 팀원들과 커뮤니케이션을 하여 진행하였고, 제 역할은 Account CRUD 구현이였습니다.
https://github.com/megar0829/kdt-final-team3
사용기술 : Python3, Django, Git, JavaScript, Css, HTML
개발기간 : 3주
도전과제 : 유저가 글, 댓글을 쓸 때 경험치가 오르는 기능 구현

프로젝트명

메이플스토리 유저 장비 아이템 검색 사이트 [Ma-ple]

소속/기관명

1인 개발

프로젝트 기간

2024.02. ~ 2024.02.

프로젝트 내용

게임 메이플스토리 유저의 장비를 검색 할 수 있는 사이트입니다.
닉네임을 입력할시 유저의 아이템 정보가 출력되도록 합니다.
https://github.com/S05p/Ma-ple-open-api-test
사용기술 : Python3, Django, JavaScript, Css, HTML
개발기간 : 2일
도전 과제 :

  1. get 형식으로 json 데이터, ocid 데이터를 받아서
  2. ocid 데이터로 다시 유저의 아이템 정보를 get

포트폴리오

URL

link

Github Spring 개인 프로젝트

깃허브
link

Github 개인 프로젝트

깃허브
link

개인 프로젝트 문제 해결 기술 블로그

URL 링크
link

Github

깃허브

교육

소속/기관명

단국대학교

종류 | 전공

대학교(학사) | 수학과

재학 기간 | 재학 상태

2015.03. ~ 현재 | 재학 중

소속/기관명

배재고등학교

종류 | 전공

고등학교 | 자연계열

재학 기간 | 재학 상태

2012.03. ~ 2014.02. | 졸업

외국어

외국어명

영어

점수

비즈니스 회화 가능

자격증

자격증명

OPic

점수 | 발급기관

IM2 | OPic

취득연월

2022.08.

댓글