zip

=> 순회 가능한 시퀀스형이나 이터레이터형 객체들을 결합하여 쌍으로 순회가능한 이터레이터 객체를 얻을 수 있다.

x = [10,20,30]
y = ["a","b","c"]

for i in zip(x,y):
	print(i)
    
(10, "a")
(20, "b")
(30, "c")

list(zip(x,y))

list
[(10,"a"),(20,"b"),(30,"c")]

range() -> 수열의 생성이다.

range(시작,끝,증가값)

대신 시작값과 증가값은 따로 넣지 않아도 된다.

list(range(10))

=> [0,1,2,3,4,5,6,7,8,9]

list(range(1,10,2)

=> [1,3,5,7,9]

 

리스트 항목과 인덱스 값을 동시에 얻는 법

enumerate()

=> 튜플로 해당 인덱스의 값과 리스트값을 동시에 반환

li = ['apple', 'see']

for i in enumerate(li):

    print(i)

(0, 'apple')

(1, 'see')

 

filter

filter(필터링할 방법, 특정 집단)

ex

li = [10,20,30]

NewL = list(filter(lambda i: i >20, li)

NewL 

[30]

 

map

=> 시퀀스형 객체를 순회하면서 모든 값을 갱신해야 할 때 사용

li = [1,2,3]

LI = list(map(lambda i: i+10), li))
LI
[11, 12, 13]

 

 

사람들은 여러가지 목적에서 저마다 자신의 일을 해나간다.

꼭 목적이 없어도 되지만 그 목적이 있을 때 효과는 2배 3배가 되는 것 같다.

나에게 올해는 WHY로 시작해서 WHY를 중요시 하는 회사에 입사해서, WHY를 물으며 끝난 해였다.

그 동안 살아오며 2019년 29살이 되기까지 개발을 접해본 적이 없다. 그 동안 해왔던 일은 학교를 다니며 집에서 수확한 농산물을 온라인으로 팔고, 또 대구 동대구 시장이라는 곳에서 쌀가게를 2년 운영했다. 

청춘정미소를 함께 한 가족들

우리만의 차별화된 서비스로 고객도 많았고, 프렌차이즈로 키우고 싶었는데.역량이 많이 부족했던 것 같다.

시작할 때는 엄청 어렵게, 그리고 자리 잡을 때는 더 쉽지 않았는데 일이 마무리되려고 하니깐 순식간에 마무리되었다. 빚도 생겼다. 그래도 이번 사업을 하면서 크게 2가지를 생각할 수 있었다. 

먼저 옛날에는 성공을 정말 어린 나이에 하고 싶다고 생각했는데, 꼭 그렇지 않아도 되겠다고 생각했다. 비록 적지만 같이 하는 가족들이 많을 때는 4명이나 되었고, 나의 잘못된 선택으로 회사를 힘들어질 때 책임감을 많이 느꼈다. 그리고 다음 사업은 언제하는지 상관없이 정말 잘하고 싶다는 생각을 하였다. 그리고 그러한 역량을 키워야 되겠다고 다짐했다.

그리고 두 번째는 다음 사업은 꼭 소프트웨어 서비스 사업을 해보고 싶다는 마음가짐이었다. 애당초 사업을 시작한 이유가 많은 사람들에게 좀 더 나은 삶을 주고 싶다는 생각으로 시작하였고, 열심히 오프라인 쌀 가게를 운영해보니 우리 매장 주변의 2000분에게 좀 더 나은 삶을 제공해줄 수 있었다.(비록 작은 쌀가게였지만 그런 사명감으로 일을 했었다.) 이 매장을 10년 운영하여 매장이 20개 30개가 되면 4만명 5만명의 사람들에게 더 좋은 삶을 줄 수 있으리라. 하지만 나는 욕심이 더 많았나보다. 소프트웨어 기술로 세상을 변화시키는 회사들을 보며 그것을 동경했다. 그리고 나는 직접 소프트웨어를 배워야겠다고 다짐했다.

사실 마음 먹은 것은 2018년도 1월이었지만 나는 용기도 돈도 없었다. 현실이었다. 공부를 어떻게 해야할지도 무슨 공부를 해야할지도 몰랐다. 그러던 찰나에 그 동안 친하게 지냈던 대표님이 함께 일해보자고 제안해주셨다.(사실 형 동생 사이로 지내며 매우 친한 사이이다) 나를 인정해주고 찾아주는 곳으로 가는 것은 사람의 본성일까? 나를 옆에서 지켜봐주고 그 동안의 모습들을 인정해주었고, 나를 필요로 하였다. 마침 아이템도 너무 관심이 있던 아이템이라 개발공부를 할 수 있는 돈도 벌겸 일을 시작했다. 그리고 회사에 들어간지 딱 2달 뒤 출시 직전에 개발팀이 단체로 퇴사하며, 나와 대표님 딱 2명만 남게 되었다. 결국 우리가 열심히 준비했던 아이템의 빛조차 보지 못했다.

'지금 나도 나가야해...' 생각은 들었지만 차마 쉽게 나갈 수 없었다. 지금 내가 나가면 정말 회사가 문을 닫을 수도 있겠다는 생각이 들었다. 1월에 회사를 마무리하며 너무나도 힘들었던 시간을 보냈어서, 대표님이 그러지 않았으면 좋겠다는 생각이 더 컸나보다. 그렇게 대표님과 나 2명이서 미친듯이 일을 했다. 할 수 있는 일들부터, 아니 우리가 생존해 갈 수 있는 일들부터 시작했다. 그렇게 2018년도는 대표님과 다시 회사를 일으키기 위해 고군분투하였다. 다행히 일을 성공적으로 풀렸다. 회사 직원이 다시 5명이 되었고, 회사는 다시 정상 궤도에 올라갈 수 있었다. 

회사는 들어가는 것도 어렵지만 좋게 마무리하는 것도 쉽지 않다. 하지만 우리의 마무리는 아주 깔끔했다. 나는 이제 내가 할 수 있는 일을 다했다고 생각했고, 대표님과 이야기 한 후 12월 31일부로 회사를 잘 마무리할 수 있었다. 그렇게 2019년 1월 1일부터 나의 서울 생활이 시작되었다.

집도 구하지 않았었고, 어떻게든 내가 누울 공간 한 군데는 있겠지라는 생각으로 무작정 서울로 올라왔다. 나의 첫 목적지는 성수역이었다. 성수역에 위치한 코드스테이츠 부트캠프를 수강할 예정이었기에(그때 당시에는 여러 부트캠프가 있는지 조차 몰랐다.) 무작정 성수역으로 향했다. 

다행히 하늘이 나를 버리진 않았나 보다. 정말 좋은 가격에 내가 누울 수 있는 장소를 찾았다. 비록 장소는 누우면 움직일 자리가 없을 정도로 좁았지만(화장실까지 합쳐서 3평정도 되었다.) 그래도 잠잘 자리가 있다는 것에 감사했다. 어차피 매일 나가서 공부하고 잠만 잘 생각이여서 별로 상관없었다.

나의 서울에서의 첫 집(성수)- 정말 보이는 부분이 전부이다.

내가 개발자로 공부하는데 있어서 정말 중요했던 것은 바로 시간이었다. 그 동안 모아놓았던 돈들은 9월이 되기 전에 무조건 떨어질 예정이었기에, 그 전에는 반드시 취업을 해야되겠다고 생각했다. 그러던 찰나에 아군이 생겼다. 계속 소프트웨어쪽으로 같이 도전해보자고 꼬셨던 친구가 한명 있었는데, 내가 서울로 올라오고 얼마되지 않아 이 친구도 무작정 서울로 올라왔다. 목적지는 나의 집이었다.ㅋㅋㅋ 나도 정말 대책이 없지만 이 친구는 더 대책이 없었다. 그래도 어쩌겠는가? 나 믿고 올라온 친구인데 내보낼 수도 없고 그렇게 우리의 이상한 동거가 시작되었다. 

우리는 최상의 시간 관리를 위해 노력했다. 1월과 2월은 PRE 과정이라고 해서 온라인으로 수강을 진행하는 것이라, 실제적으로는 개인공부를 진행하였다. 그렇기에 시간 관리는 더더욱 중요하였다. 움직이는 시간을 최소화 하였다. 그것은 바로 한 건물에서 모든 것을 해결하는 것이었다. 성수역에 위치한 한 빌딩의 코워킹스페이스에서 우리는 공부하기로 결정했고, 그 빌딩의 지하 2층 헬스장을 끊었다. 그리고 점심은 그 빌딩의 2층에 위치한 한식 뷔페에서 해결했다. 우리의 일과표는 심플했다.

매일 5시 30분 기상, 6시까지 헬스장 도착. 그리고 6시부터 7시까지 헬스(체력관리), 7시부터 공부시작해서 11시 30분까지 공부를 하였다. 그리고 11시 30분부터 점심식사, 12시부터 다시 공부시작. 6시에 집에서 싸온 샐러드를 먹고 10시까지 공부. 너무나도 심플한 일과였다. 이 일과를 정말 매일 매일 하기 위해 노력했다.

그래서 우리가 정말 엄청난 양의 공부를 했을까? 사실 나와 나의 친구는 평범한 사람이다. 문제는 너무 많았다. 우리는 너무 재미있는 것을 많이 알고 있었고, 개발 공부가 처음이었으며, 돈이 있었고, 항상 개발공부를 하기 위해서는 노트북이 필요했다. 그것에 비해 온라인으로 혼자 공부해가는 과정은 너무 지루하고, 실력이 늘고 있다는 생각조차 쉽게 들지 않았다. 

"풋살 ㄱ?" 이 한마디에 우리는 곧장 풋살 경기가 있는지 확인했고, 미친듯이 집에 가서 옷을 챙겨 풋살장으로 향했다. 그렇게 풋살 경기를 하러 갈 때 우리는 제일 신났고, 풋살은 개발보다 재미있었다. 개발 공부를 처음 시작하기에 모든 것이 모르는 것 투성이었고, 우리는 어쩔 수 없이 구글링과 유튜브를 뒤졌다. 그리고 구글은 우리를 너무 잘 안다. 우리에게 너무나 적합한 컨텐츠를 잘 추천해준다... 그렇게 움직이는 시간을 최소화한 것이 의미가 없이 우리는 유튜브를 시청했다.

우린 나약했지만, 그래도 작심을 잘했다. 우리의 문제점을 파악했고 개선하기 위해 노력했다. 풋살 가방을 가질러 가는 일이 없도록 미리 공부하는 곳에 배치해두었고, 풋살장에 가기 위해 필요한 시간들을 최소화하였다. 그리고 역시 공부는 책으로 해야한다며 책도 구매하였다. 그리고 3일 뒤에는 또 마음을 먹었다. 하루 종일 공부하는 일은 너무 너무 어렵고 지루한 시간들이었다.

코딩 자신감에 대한 그래프 (00책 참고)

나는 이 책을 읽으면서 정말 이 그래프가 너무 잘 맞다고 생각했는데, 나도 사실 프로그래밍이라는 것을 처음 시작 했을 때 내가 코딩을 잘 한다고 잠시 착각을 하였었다...  그리고 혼자 공부를 본격적으로 시작하면서 곧 당황하는 단계를 맞이하였고, 절망하였다.

"내가 잘하고 있는 것이 맞는 것인가?"

"이렇게 하면 비전공에 개발을 처음 시작하는 내가 취업을 할 수 있을까" 등등

수없이 많은 나쁜 생각들이 나를 괴롭혔다. 그리고 그 절망하는 단계는 생각보다 깊고 오래 간다. (그래서 많은 사람들이 이 시기에 개발을 많이 포기하게 된다고 책에서 설명하고 있다.) 나 역시도 그랬지만 뒤가 없었다. 어떻게든 개발자가 되어야 했고, 그래야만 나의 생활이 가능했기에 일단 개발을 계속 할 수 밖에 없었다.

그렇게 나는 3월 달에  본격적으로 부트캠프라는 것을 시작하게 되었다. 코딩 부트캠프는 단기간에 개발자로 양성해주는 학원정도로 생각하면 쉽겠다. 온라인 과정으로 코드스테이츠를 시작했지만 나는 패스트캠퍼스 스쿨 과정으로 서버 과정을 듣게 되었다. 내가 부트캠프를 바꾸었던 무엇보다 큰 이유는 바로 "딥러닝"에 대한 방향성이었다. 지금에서야 이 분야로 한번에 가는 것이 쉽지 않다는 것도 알고, 내가 얼마나 노력을 더 해야하는지도 알지만 그때 당시에만 하더라도 기회가 된다면 딥러닝 분야로 바로 시작을 하고 싶었고, 그럴려면 최소한 "파이썬"이라는 언어를 공부해야 했다. 내가 알고 있었던 것은 딱 그정도였다. 정말 딱 '딥러닝 분야로 가려면 일단 파이썬을 해야 해' 정도...

그에 비해서 코드스테이츠는 자바스크립트을 바탕으로 수업을 진행하고 있었고, 파이썬으로 부트캠프를 진행하는 곳은 패스트캠퍼스 스쿨 과정이 전부였다. 파이썬으로 서버 개발자를 양성하는 패스트캠퍼스 웹프로그래밍 스쿨 과정이었다.

그리고 나와 함께 하던 친구는 "코드스쿼드"라는 곳에서 새롭게 부트캠프를 시작하기로 결정하였고, 이상했던 동거? 역시 마무리되었다.

3월부터 본격적인 수업이 시작하기에 앞서서 "점프 투 파이썬" 이라는 책으로 파이썬 기본 문법을 3번 정독하였다. 그리고 나는 새학기 첫 학교를 가는 것처럼 설렌 마음으로 3월 4일부터 수업을 받게 되었다.

수업은 6개월 과정이었다. 크게 2개월은 파이썬 기본 문법 및 컴퓨터 사이언스 강의, 2개월은 프레임워크에 대한 강의 그리고 마지막 2개월은 팀 프로젝트로 나누어져 있었다. 

결과적으로 나는 3월 2일부터 시작해서 프레임워크가 끝난 4개월이 지난 뒤 부터 취업 준비를 하였고, 7월 22일부터 다노에 입사해서 개발자로 시작할 수 있었다.

아래에 내가 공부해왔던 과정들에 대한 첨부!

1편 [내가 프로그래밍을 시작하게 된 이유 :: 쌀 팔다 개발자](https://daeguowl.tistory.com/2)

 

내가 프로그래밍을 시작하게 된 이유

나는 현재 패스트캠퍼스 웹프로그래밍 스쿨에 다니고 있다. 3월 4일부터 그 과정을 시작하여 벌써 어느새 2달이 다 되어간다. 이 글들을 좀 더 빨리 적었으면 좋았겠지만, 그래도 어느 정도 경험을 해보고 적는 것..

daeguowl.tistory.com

2편 [웹프로그래밍스쿨) 패스트캠퍼스 스쿨 수업 후기 :: 쌀 팔다 개발자](https://daeguowl.tistory.com/11?category=796233)

 

웹프로그래밍스쿨) 패스트캠퍼스 스쿨 수업 후기

지난 주를 기점으로 패스트캠퍼스 스쿨 수업 대부분이 끝났다! 공식적으로는 끝나지 않았지만 전체 6개월과정중 4개월정도 지나갔고, 이제 남은 2개월 동안은 개인 및 팀 프로젝트를 진행하는 기간이라, 전체적인..

daeguowl.tistory.com

3편 [패스트캠퍼스 웹프로그래밍 스쿨를 마무리하며 :: 쌀 팔다 개발자](https://daeguowl.tistory.com/17?category=796233)

 

패스트캠퍼스 웹프로그래밍 스쿨를 마무리하며

이 이야기는 올해 1월 개발을 처음 시작한, 그리고 3월부터 패스트캠퍼스 웹 프로그래밍 스쿨에 대한 이야기의 마지막 이다. (+취업 이야기의 연장) 저마다 개발을 하는 이유는 있을 것이다. 그리고 늦은 나이에..

daeguowl.tistory.com

 

분명 부족했지만 너무나도 좋은 기회로 일을 시작하게 되었고, 결론적으로 첫 시작은 인턴이었지만 2020년 1월 1일부로 정식으로 다노 크루로 합류하였다. 예!!!!!

다노는 정말 너무 좋으신 분들이 많고, 항상 자극을 받고 더 열심히 하려고 노력한다. 다노는 "why가 정말 중요한 회사이고" 그 "why"가 명확하기에 다들 너무 열심히 일을 하신다. 보통 회사에서의 일은 적당히 하려고 노력하지만 다노에서 내가 느낀 것은 "정말 누구나 열심히 하는 회사" 였다. 이 모든 사람들이 하나의 비전을 가지고 나아가니, 회사가 성장하지 않을 수 없는 것 같다.

그리고 이런 곳에서 나의 개발자 커리어를 시작할 수 있어서 너무 행복하다. 

나의 첫 개발자 생활이 "다노"라서 

정말 다행이다. 올 한해는 꼭 다노에서 "인정 받는 개발자"로 성장하고 싶다.


그래서 2020년 과연 쌀 팔다 개발자는 다노에서 인정 받는 개발자가 되었을까요?
=> 쌀 팔다 개발자 2020년 회고

 

쌀 팔다 개발자 2020년 회고

올해 내가 어떤 목표를 세우고, 어떤 challenge를 하고, 어떤 생각을 하면서 살았는지는 하루 하루를 보면 생각보다 보이지 않는다. 그렇지만, 1년이 끝난 뒤에 명확하게 보여진다. 매일 매일은 크

daeguowl.tistory.com

2019.08.08 git으로 보는 소스 코드 관리

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. git

  • ㅠㅠ.. 분명 며칠 전에 와 거이다 이해했다하고 글을 적었었는데... 그 사이에 또 굉장히 많은 것들을 알게 되어서 다시 정리한다.
  • 그리고 이 글도.. 아마 며칠 뒤면 또 새로워질 것 같아서 걱정이 된다.
  • git은 쉽지 않다.

전체적으로 보면 Origin과 Local이 있다.

  • 일단 전체적인 것을 보는 것이 중요하다.
  • 우리 회사에서는 소스코드 관리를 bitbucket으로 진행되고 있다.
  • 그리고 그 bitbucket에도 origin master와 origin branch가 있다.
  • 내가 이용하고 있는 로컬에도 local master와 local branch가 있다. 이것을 이해하지 못하면 계속 봐도 이해가 안된다.

그림으로 보는 git

스크린샷 2019-08-08 오전 9 22 04
  • 기본적으로 local master의 경우 origin master를 pull해와서 생성한다.
  • local에 해당 파일을 만들었다면 일차 목표는 성공했다.
  • 이제 작업을 하기 위한 과정들을 하나씩 살펴보자

local에서 작업하기

  • local에서 작업하기 위해서는 branch를 따주어야 한다.
  • 그 명령어는 git branch [브랜치명]이다.
  • 보통은 그 이슈가 주어진 명과 비슷하게 따면 좋다.
  • 그 다음에는 git checkout [브랜치명]을 통해 해당 브랜치로 이동한다.
  • 해당 브랜치에서 이동을 한 이후에 작업을 진행 해주면 된다.
  • 작업 도중에 다른 브랜치로 이동하려면 git add 와 git commit을 통해 local에 저장을 시켜줘야 한다.

local에서 작업이 끝난 이후

  • 다시 local master브랜치로 이동을 한다.
  • 먼저 git add와 git commit이 되어야 한다.
  • local master로 이동한 이후에 git status를 확인해보면 그동안 origin master에서 쌓인 다른 작업들을 확인할 수 있다.
  • 일단 그 local master와 origin master를 하나로 합쳐 주어야 한다.
  • local master에서 git pull origin master명령어 실행
  • 그러면 local master와 origin master가 같아진다.
  • 이후에 다시 해당 브랜치로 이동한다.
  • git checkout [브랜치명]

해당 브랜치에서 rebase해주기

  • rebase를 통해서 그 동안 master에서 올라왔던 commit들과 현재 내가 branch에서 했던 commit들을 다시 잘 정렬해준다.
  • rebase 혹은 merge를 해줘도 된다.
  • 근데 merge에 대해서는 다시 한번 생각해봐야 되겠다.

해당 브랜치에서 origin branch로 push 해주기

  • 해당 브랜치에서 git push를 해주면 origin branch로 올라가게 된다.
  • 올리기 전에 git status를 해보고 여러개의 commit들이 꼬여있다고 하면 git push -f를 해주면 된다.
  • 하지만 아마 보통 origin에서 브랜치가 없을 예정이다.
  • 그러면 밑과 같은 경고 메시지가 나온다.

스크린샷 2019-08-07 오전 11 45 30

  • 해당 문구는 origin에 브랜치가 없다는 말로, origin에 올리면서 해당 브랜치도 함께 만들겠다는 명령어이다.
  • 위에 내와 있는 git push --set -upstream origin [브랜치명]을 입력해주면 된다.

origin branch에서 origin master로 pull request 보내주기

  • 이때는 source tree를 사용하여 origin master로 pull request를 보내준다.
  • 아마 회사마다 사용하는 툴이 있을 것이므로 위 과정을 실행해주면 된다.
  • 이제 이때부터 origin master에 merge되기 위해 코드리뷰 등이 시작된다.
  • pull request 좀 받아주세용 ㅠㅠ.

'GIT' 카테고리의 다른 글

GIT 04. Git stash와 git  (0) 2019.12.30
GIT 03. Merge & Rebase  (0) 2019.12.30
GIT 02. GIT 브랜치  (0) 2019.12.30
GIT 01. GIT 한번에 이해하기  (0) 2019.12.30

2019.08.01 git stash와 git에 대해서

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. git

  • git에 대해서 잘못 알고 있었던 부분들이 좀 있었던 것 같다. 그동안 크게 협업을 할 일도 없었고, 크게 브랜치를 섞어가면서 쓸 일도 없었고, git은 그저 소스코드를 관리해주는 용도로만 생각했다. 음 내가 생각한 git은 github(원격저장소)에 가깝다고 하는 게 맞을 것 같다.

잘못된 브랜치에서의 작업

  • 하나의 브랜치에서 한개의 작업을 한다. 그리고 이것에 대해서 너무나도 쉽게 잊어버렸다. ㅎㅎ
  • 가장 첫 번째 작업을 하나의 브랜치에서 작업을 했다. 그리고 commit와 push까지 완료하였다.
  • 하지만 나는 새로운 브랜치를 만들지 않고, 현재의 브랜치에서 또 작업을 진행했다....
  • 평소 같았으면 또 commit과 push를 했을테지만 여기는 회사다 ㅠㅠ
  • 당황한 나는 새로운 브랜치로 옴겨가려고 하였다. 그리고 git checkout master 명령으로 master브랜치로 가려고 하니, 에러 메시지가 발생하였다.

8E426D7E-4E84-4705-ACBC-1EF7209FE1BC

  • 이 메시지를 보고 신나게 checkout -- <파일>을 입력하였다.
  • 그리고 branch를 이동하였다.
  • 근데 다시 작업하던 브랜치로 이동하였을 때 그동안 내가 해놓은 코드들은 모두 사라졌다....
  • 무슨 일????? ㅠㅠㅠㅠ

git은 형상관리를 해준다.

  • 이게 무슨 일인가 당황해하고 있는 나에게 사수분께서 설명해주셨다.
  • git은 형상관리를 하기 때문에 소스의 변화를 끊임없이 감지하고 있고, 위에 git checkout -- <파일>을 진행함으로서 내가 그동안 작업한 것을 모두 되돌린다는 의미이다. 왜냐하면 git commit을 해주지 않았기 때문이다. git add 와 git commit을 통해서 local에 내가 진행했던 코드들에 대해서 저장을 했어야 했는데 하지 않았기 떄문이다 ㅠㅠ.
  • 또 추가적으로 알게 된 것은 이렇게 잘못된 브랜치에서 작업했을 경우 git stash라는 명령어를 활용할 수 있다는 것이다.

git stash

  • git stash는 내가 브랜치에서 작업하다가 다른 branch로 옴겨야 하는 경우가 생겼을 때 commit하지 않고 잠시 작업하던 것을 보관하고 다른 branch로 넘어가는 것이다.
  • 나는 그동안 당연히 !!! 너무나 당연히 git stash처럼 된다고 생각했다. 내가 작업했던 중에 다른 브랜치로 이동해도 내 화면에는 그대로 내가 작업하던 것들이 남아 있으리라 생각했다.
  • 하지만 git은 브랜치 단위로 관리되고 각 branch마다 정말 소스코드가 관리되고 있었다. 즉 각 브랜치로 이동할 때 마다 내 화면에도 소스코드가 모두 변한다는 것이다. 그 동안 진행하였던 프로젝트는 크지 않아 크게 느끼지 않았는데 정말 브랜치를 이동해보니 각각의 코드 상황이 달랐다.
  • 따라서 만약에 해당 상황이 발생하면 git stash를 기억하자

git stash 명령어

  • git stash : 하던 작업을 저장하고 가장 최근 commit상태로 만든다.(임시저장)
  • 해당 브랜치 이동
  • git stash pop 또는 git stash apply : 저장되어 있는 작업 중 가장 최근 stash를 가져와서 적용
    • git stash pop은 stash list에 남지 않고 stash apply는 stash list에 남는다.
  • git stash list : stash 목록을 본다.

'GIT' 카테고리의 다른 글

GIT 05. git으로 보는 소스코드 관리  (0) 2019.12.30
GIT 03. Merge & Rebase  (0) 2019.12.30
GIT 02. GIT 브랜치  (0) 2019.12.30
GIT 01. GIT 한번에 이해하기  (0) 2019.12.30

2019.07.29 MERGE & REBASE

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. git rebase란?

  • 깃에서 작업을 위해 master에서 새로운 branch를 따서 작업을 할 때, 협업상황에서 다른 사람이 master에 commit하는 상황이 발생하게 된다. 그런 경우 현재 내가 작업하고 있는 master는 현재의 master와는 다른 상황이 발생하게 된다. 이 경우 rebase를 통해 이때까지 commit된 상태의 최상단으로 현재 내가 작업하고 있는 master를 옴기는 것을 rebase라고 한다.

merge 와 rebase

  • merge 와 rebase 의 경우 모두 git의 코드를 합치는 방법인데 탕수육의 부먹과 찍먹과 같이 서로 추구하는 방향에 따라서 다르게 사용한다고 하였다.
  • 각각의 장단점에 대해서 살펴보자.

merging의 장점

  • 이해하기 쉽다.
  • 원래 브랜치의 컨텍스트를 유지한다.
  • 브랜치 별로 커밋을 분리해서 유지해준다.
  • 원래 브랜치의 커밋들은 변경되지 않고 계속 유지되어 다른 개발자들의 작업과 공유대는 것에 대해 신경 쓸 필요가 없다.

merging의 단점

  • 모든 사람들이 같은 브랜치에서 작업하기 때문에 머지해야 할 때는 merge가 커밋 히스토리상으로 전혀 유용하지 않고 어지럽기만 하다.

rebase 장점

  • 단순한 히스토리
  • 여러 개발자들이 같은 브랜치를 공유할 때 커밋을 합치는 가장 직관적이고 깔끔한 방법

rebase 단점

  • 충돌상황에서 다소 복잡하다. 커밋 순서대로 rebase를 하는데, 각 커밋마다 충돌해소를 순서대로 해주어야 한다. SourceTree가 가이드하기는 하지만, 역시 복잡한 것은 사실이다.
  • 해당 커밋들을 다른 곳에 푸시한 적이 있다면 히스토리를 다시 쓰는 것에 부작용이 발생한다. Mercurial에서는 간단히 푸시를 할 수 없다. git에서는 push할 수 있으나 나 혼자 쓰는 리모트 브랜치에서만 가능하다. 만약 다른 사람이 그 브랜치를 체크아웃 받은 후 내가 리베이스 한다면 꽤 혼란스럽게 될 것이다.

결론

  • rebase와 merging 모두 각자의 가치가 있어서 상황에 따라 다르게 사용해야 한다.

Git Merging 과 Rebase 의 상황별 사용법 – ElegantCoder

'GIT' 카테고리의 다른 글

GIT 05. git으로 보는 소스코드 관리  (0) 2019.12.30
GIT 04. Git stash와 git  (0) 2019.12.30
GIT 02. GIT 브랜치  (0) 2019.12.30
GIT 01. GIT 한번에 이해하기  (0) 2019.12.30

2019.04.10 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 원격 저장소

원격 저장소는 소스코드와 버전을 백업 및 관리하고, 다른 사람과의 협업이 가능하도록 도와주는 기능이다.

  • local repository & remote repository
  • 원격저장소의 중요한 의미 2가지
    • 소스코드를 백업한다.
    • 다른사람과 협업한다.
  • 참고 : Git - 리모트 브랜치

원격 저장소를 지역 저장소로 복제

clone 명령어는 다음과 같습니다.

  • git clone { 원격 저장소 URL }

특정 브랜치를 clone 하고 싶다면,

  • git clone -b { 브랜치명 } { 원격 저장소 URL }

지역 저장소에서 브랜치 작업 시작

현재 브랜치의 상태 확인

  • git branch : 지금 현재 어느 브런치로 설정되어 있는지 확인
  • git branch -r : remote branch 확인(원격저장소 브랜치 확인)
  • git branch -a : 모든 브랜치 상태 확인

브랜치 생성

git branch feat/loop

  • master 브랜치 밑에 feat/loop 브랜치 생성
  • 확인 : git branch로 확인을 해보면 feat/loop 브랜치 생성
  • 아직까지 remote에는 생성되지 않았다. (commit을 해줘야 생성된다.)
  • 브랜치 생성만 해주면 github에서 인식 불가

브랜치 이동

git checkout <브랜치 이름>

  • git branch를 통해 실제로 이동했는지 확인한다.

브랜치를 원격 저장소로 전송

git push -u origin feat/loop

  • 새로운 브런치를 만들고 처음 원격 저장소로 업로드 할 때는 -u를 해줘서 업스트링 셋을 해줘야 한다.
  • 한번만 경로 설정 이후에는 git push를 통해 진행을 해도 가능하다.
    • 안해주면 10분정도 혼날 수 있는 각이다.

브랜치 밑에 새로은 브랜치 생성

git checkout feat/loop           # 새로운 브랜치 바로 위의 브랜치로 이동
git branch feat/con-state        # 새로운 브랜치 생성
git branch                                 # 생성 확인
git checkout feat/con-state        # 새로은 브랜치로 이동
스크린샷 2019-04-10 오전 11 26 01

MERGE (당긴다)

feat/con-state를 feat/loop 로 옴기기

  • 당긴다고 생각하면 좋다.
  • 당길 곳으로 이동해서 그 당길 것을 당긴다.
  • feat /loop로 이동해서 feat/con-state를 당긴다.
git checkout feat/loop
git merge feat/con-state

# feat/loop 입장에서는 feat/con-state 내용이 들어온 것이므로 commit이 한개 생긴다.

git push -u origin feat/loop
스크린샷 2019-04-10 오전 11 31 07

마스터에서 merge 해주기

git checkout master        #마스터 브랜치로 이동
git branch                        #실제 이동 상태 확인
git merge feat/loop            # feat/loop를 마스터 브랜치로 merge
git status                            #현재 git 상태확인
git push origin master           # origin master로 전송
AC062E9B-7327-4645-8C2C-8A1DC56AC96D

브런치 지우기

우리가 실제로 작업 할 때 기능 개발이 완벽히 끝났을 때는 브랜치가 남아 있으면 안된다.

git branch -D <브랜치 이름>

우리가 개발하는 모든 부분은 DEV이라는 브런치를 따서 만들고
master에는 고객들이 쓰는 상용화 된 것만 놔둔다. 우리는 항상 master를 쓸 일이 잘 있지 않을 것이다.

master를 지워도 타임스탬프로 돌아가면 된다.
뭔가 이유 없는 테이블은 없다. DB를 훔쳐보면 안된다.

git flow strategy

참고 : 티몬의 개발이야기 : 네이버 블로그

스크린샷 2019-04-10 오후 12 20 02
  • 메인 공간은 사용자만 쓰는 것으로 놔둬야 한다.
  • git flow를 통해서 브랜치 관리를 쉽게 사용할 수 있다.
  • git-flow : git-flow cheatsheet
  • git init 이후에 바로 git flow init 으로 git flow 도 쓸 것이라고 해준다.
  • git flow init을 설정까지 하고 나면 develop가 알아서 생성되고 들어가준다.
스크린샷 2019-04-10 오후 12 20 32
  • git flow init 이후에 feature,release,hotfix 각각에 start/ finish / publish/ pull 사용 가능
  • git flow feature start <이름> ==> 해당 브런치를 생성해주고 해당 브랜치로 이동까지 해준다.

release란?

  • 배포를 위한 버전 관리
  • git flow release start/finish/publish/pull 가능

v.0.0.1.00190410001

릴리즈 피니시를 하면 merge에 관한 것 1개와 release 작업 물에 대한 태그를 달 수 있는 2개가 나온다.

릴리즈 절차를 끝내면 마스터와 develop에 commit이 남는다.
github에서 확인하기 위해서는 마스터 및 develop에서 push 해줘야 한다.

89512A50-75B9-4255-BEEB-EF7AAB1C7277

git 을 기반으로 git-flow를 사용하여 애플리케이션 배포버전을 관리하자.

git clone, pull, fetch의 차이

  • 이해하기에 좋은 글이 있어서 그대로 가지고 옵니다.

[참고 : Git 개념] remote, push, clone, pull :: victolee

git pull 명령어 역시 Github에 있는 파일들을 local로 가져오는 것입니다.
git clone은 Github의 모든 파일들을 가져오기만 하는 것이고,
git pull은 local repository에 저장( add )까지 되며, 현재 local repository와 비교까지 합니다.
다시 말하면, git pull = git fetch + git merge 와 같습니다.
git fetch는 local에 연결된 remote repository의 브랜치 목록과 그 파일 내용을 가져오는 역할, 즉 업데이트를 하는 명령어입니다.
git merge는 나중에 알아볼 명령어인데, 두 개의 branch를 병합해서 하나의 코드로 만드는 명령어입니다.
즉, git pull은 협업 과정에서 최신 코드로 업데이트 하는 용도로 많이 사용합니다.
예를 들어, 팀 프로젝트를 진행하다가 친구가 기능 구현을 완료해서 git push를 했다고 가정해보겠습니다.
저는 새로운 버전을 사용하기 위하여 Github에 있는 코드를 가져와야 하는데 어떤 방식으로 가져와야 할까요?
git clone을 하면 제가 그동안 작업했던 내용들과 최신 버전의 파일은 독립된 존재가 되어버립니다.
즉, clone을 해서 받은 폴더에 제가 작업했던 내용들을 일일이 수작업으로 적용시켜야 하죠.
이는 좋은 방법이 아닙니다.
git pull을 하면 어떨까요?
현재 제가 작업 중인 local repository와 최신 코드가 비교되고 병합되어, 최신 버전 파일들이 저의 local repository에 적용됩니다.
따라서 제가 작업하고 있던 코드들과 최신 버전 파일의 코드는 함께 존재하게 되죠.
즉, 제가 작업한 코드와 친구의 작업 코드가 자동으로 합쳐지게 되니까 매우 바람직합니다.
그런데 만약 동일한 파일 내역을 수정했다면 어떻게 될까요?
이 경우 충돌( conflicts )이 발생하며 일일이 충돌된 부분을 수정해야 합니다.
이 부분은 다음 글에서 merge를 할 때 다루도록 하겠습니다.
정리하자면,
git clone은 완전히 새로운 프로젝트에 투입 되었을 때,
git pull은 작업하면서 최신 버전을 가져올 때
사용한다고 생각하시면 좋을 것 같습니다.

'GIT' 카테고리의 다른 글

GIT 05. git으로 보는 소스코드 관리  (0) 2019.12.30
GIT 04. Git stash와 git  (0) 2019.12.30
GIT 03. Merge & Rebase  (0) 2019.12.30
GIT 01. GIT 한번에 이해하기  (0) 2019.12.30

2019.04.04 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 깃의 흐름에 대해 이야기해보세요
git process
  • 알면 좋은 것
    • 깃허브 - 깃을 기반으로한 소스코드 버전관리
    • 빅버킷 - 5인 이하의 팀에서 비공개 저장소가 무료
    • 깃랩 - 로컬에 사설 저장소를 만들고 이용할 수 있도록 해준다.

목적 : git remote에 보내기 위해 송장을 완성한다.

git init : 택배 부치기로 마음 먹음

  • index와 local reprository가 생겨나게 된다. 내가 앞으로 git으로 버전관리를 시작하겠다.

git init도 지우고 싶다고 하면 :

  1. ls - al에서 .git이 있는 것을 지워준다.
  2. rm -rf .git

무조건 상위에 있는 git을 따라가기 때문에 가장 상위에서 git을 하게 되면 난리난다. 따라서 내가 정말 관리하고 싶은 것만 해야 한다. 이 실수를 한번 했었기 때문에 절대 하면 안된다.

git status — > 현재 깃의 상태를 확인한다.

git config —global 을 했었기 때문에 등록이 되어있다.

보내는 사람은 git config로 설정해준다. (한번만 해주면 자동등록되어 있다)

  • git config —global user.name “byeonguk kim”
  • git config —global user.email gang0406@naver.com

받는 사람에 대한 설정은 모두 git remote로 한다.

git remote 관련된 것들 확인 : git remote help

  • 받는 사람 주소 추가 : git remote add 별명 주소

  • 주소가 길기 때문에 보통 origin이라는 별명을 쓴다. 오늘은 cat
    git remote add 별명 주소

  • 실제 주소 확인:

    • git remote get-url cat 하면 실제주소 확인
  • 설정된 주소 삭제:

    • git remote remove cat

ADD 택배보내기

  • 보낼 제품 고르기

    • git add hello.py
  • 모든 제품 보내기

    • git add -A
    • git add .
  • 제품 잘 들어갔나 확인해보기

    • git status

COMMIT: 받는 사람에게 편지 첨부하기

  • git commit -m “제목 : 엔터 + 내용 "

  • git commit —>vim 이 실행됨

  • 제목 적기 (제목은 항상 클리어하게 나와야 한다- 제목만 보고도 이 당시에 무엇을 했는지 알 수 있도록 한다)

    • feat : 제목 수정
    • doc: 도큐먼트
    • fix: 파일 수정
  • 내용 적기 : 제목과 2줄 간격을 띄우고 내용 적기

  • 다시 normal 모드(esc)로 가서 :wq (저장하고 나가기)

  • 이까지 하면 local repository에 쌓이게 된다.

  • 파일 생성 및 vim으로 바로 들어가기 : touch hello.py && vi hello.py

  • tip: 꼭 동작이 되는 것을 보내야 한다. 왜냐하면 다른 사람이 보고 이상하다고 생각할 수 있기 때문이다.
    따라서 커밋을 한다는 것은 꼭 동작하는 코드를 올려줘야 한다.

  • tip

commit에 관련해서는
파이썬에서는 마지막 엘리먼트의 ,를 놔두고 쓰면 장점이 있다.
파이썬의 컬렉션을 쓸 때는 마지막에 ,를 붙여 놓고 쓴다.
그리고 엔터를 치고 나열한다.

리스트 또는 딕셔너리는

li = [1,2,3,4,]가 아니라.
===>
li = [
1,
2,
3,
4,
]

dic = {
“a” : 1,
“b” : 2,
“c” : 3,
“d” : 4,
}

하는게 좋다.

일렬로 적어 놓으면 새롭게 지우고 새롭게 추가하게 나온다.

PUSH: 우체국으로 넘겨주기(온라인)

내가 살고 있는 우체국에 꼭 넘겨줘야한다.
딱 한번만 진행하면 된다.
내가 보낼려고 하는 우체국과 받는 우체국이 같다고 알려줘야 한다.

git push -u origin master(깃허브의 별명을 넣어주면 된다.)

한번 이후에는
git push origin master (git push를 할 수 있지만 명시적으로 표시해줘야한다.)

vim의 모드

  1. normal — 켜자마자 나오는 모드
  2. insert mode —> normal에서 i를 눌러야 한다. esc는 해제
  3. visual mode —> 블록 설정 normal 에서 v를 눌러야 한다.
  4. 항상 명령은 normal에서 수행 shift + : ==> 메뉴바
    1. 빠져나가기 :q
    2. 저장하기 : wq (저장하고 나가기)
    3. 좌우이동 : hjkl 만들기

이 주소지에 몇가지를 담아놓고 clone을 해오는 것(반대로 우체국에서 가지고 오기)

특정 폴더에서 git clone https://github.com/fabl1106/first-cloned-repo.git 레파지토리 주소

ls -al을 해보면 깃허브에 있는 것을 로컬로 가지고 온 것을 알 수 있다.

LICENSE : MIT license 자유롭게 쓰세요

  • GNU GPL : gpl을 따르거나, 라이센스를 풀거나 해라.

README.md : 생성한 페이지의 메인 페이지

#Custom

gitignore : 환경 설정을 한다. 무시하게 만든다. 환경 설정을 한다.

vim 으로 접속하면 수정할 수 있다.

hidden/ #hidden 디렉토리에 있는 모든 파일을 무시한다.
.java #java로 생성된 파일을 무시한다.
.DS_Store #mac에서는 꼭 하면 좋다.
hell.
#hell이라는 이름으로 생성된 모든 확장자 파일을 삭제해라

  • 별개의 작업은 각각의 박스에 담아서 commit을 해준다.
  • 먼저 git add를 한개씩 해준다.
  • stage가 있으면 개발 커밋이 가능해진다.
  • local repository 오프라인 개발이 가능하게 해준다.

방법은 2가지이다.

  1. git init해서 commit 해놓고 나중에 repository를 만들고 commit
  2. 먼저 repository를 만들어놓고 시작한다.

'GIT' 카테고리의 다른 글

GIT 05. git으로 보는 소스코드 관리  (0) 2019.12.30
GIT 04. Git stash와 git  (0) 2019.12.30
GIT 03. Merge & Rebase  (0) 2019.12.30
GIT 02. GIT 브랜치  (0) 2019.12.30

2019.07.13 Tree

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 트리

스크린샷 2019-07-04 오후 3 16 21
  1. 어떤 노드 간에 경로가 존재해야 한다.( 즉 연결이 되어 있어야 한다) - connected
  2. 단 사이클이 없어야 한다 - acyclic
  3. set안에 tree와 tree가 있으면 forest이다
  4. 루트 노드가 없어지면 각각 분리집합으로 분할 가능

트리 용어

스크린샷 2019-07-04 오후 3 22 11
  • 레벨와 트리의 높이가 같다고 하면 한쪽으로 굉장히 치우진 것과 같다.
스크린샷 2019-07-04 오후 3 22 32

엣지와 노드와의 관계

이진트리

03C0592B-1945-4C29-BBCD-166E54B1B86E

이진트리의 특징

7E694D61-757A-46E8-B196-4A44EC01CEB2

완전 이진 트리

F8B61E1B-FD5A-4DE9-958B-A1D4DC04B71F

추가 공부할 사항

  • 포화 이진 트리
  • 편향 이진 트리

이진트리의 순회

  1. 스텍기반
    1. 전회순회
    2. 중위순회
    3. 후위순회
  2. 큐 기반
    1. 레벨 순회

전위순회(pre order)

8E201DED-3874-4ED4-BBB1-A5EC78ED01DC
class TreeNode:
    def __init__(self, data):
        self.data=data
        self.left=None
        self.right=None

def preorder(node):
    #base case
    if not node:
        return

    #노드를 방문했다고 치고 print로 찍어봄 실제로는 오퍼레이션을 넣고 방문할 떄 마다 실행
    print(node.data, end = "  ")
    # 왼쪽 자식
    preorder(node.left)
    # 오른쪽 자식
    preorder(node.right)

if __name__ == "__main__":
    n1 = TreeNode(1)
    n2 = TreeNode(2)
    n3 = TreeNode(3)
    n4 = TreeNode(4)
    n5 = TreeNode(5)
    n6 = TreeNode(6)
    n7 = TreeNode(7)

    n1.left = n2
    n1.right = n3
    n2.left = n4
    n2.right = n5
    n3.left = n6
    n3.right = n7

    preorder(n1)

중위순회(in order)

스크린샷 2019-07-04 오후 4 00 40

def preorder(node):
    #base case
    if not node:
        return


    # 왼쪽 자식
    preorder(node.left)

     #노드를 방문했다고 치고 print로 찍어봄 실제로는 오퍼레이션을         넣고 방문할 떄 마다 실행
    print(node.data, end = "  ")

    # 오른쪽 자식
    preorder(node.right)

후위순회 (post order)

스크린샷 2019-07-13 오전 9 38 25

def preorder(node):
    #base case
    if not node:
        return


    # 왼쪽 자식
    preorder(node.left)

    # 오른쪽 자식
    preorder(node.right)

     #노드를 방문했다고 치고 print로 찍어봄 실제로는 오퍼레이션을         넣고 방문할 떄 마다 실행
    print(node.data, end = "  ")

2019.07.13 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 자료구조

  2. 자료구조는 데이터베이스에 어떻게 데이터를 담을 것인지에 대한 이론이다.

  3. 서버 개발자로서 굉장히 중요한 부분이다.

1. 전체적인 흐름

  1. 동적배열(Dynamic array) vs 연결리스트(linked list)
    1. insert, delete, search에 따라서 효율이 다름
  2. BST(array list와 linked list의 단점 보완)
    1. insert, delete, search 모두 O(log(n))
    2. 트리의 깊이가 깊어지면 문제 발생
  3. 레드블랙트리(red black tree)
    1. self-balancing 기능이 생기면서 BST문제 해결
  4. B-Tree
    1. 캐시기능 추가 보완
  5. B+Tree

동적배열 vs 연결리스트

스크린샷 2019-07-13 오전 9 04 25
  1. 동적배열(파이썬에서는 list가 동적배열이다 - 따라서 append와 pop은 O(1) )
    1. 장점 : search - (O(1) : 인덱싱때문에)
    2. 단점 : Insert, delete : O(n) - 최악의 경우는 제일 앞에 것을 지우고, 삭제할 때이다. 하나씩 다 옴겨줘야 한다.
    3. 할당되어 있는 전체 크기가 capacity라고 하는데, 만약 특정 위치에 값을 넣거나 삭제하려고 하면 자리를 계속 옴겨야 한다.
    4. 만약에 capacity가 꽉 찼는데 append해야 한다면 amortized analysis (분할 상환 분석-뒤에서 참고)
    5. 예) 파이썬의 list
  2. 연결리스트
    1. 장점 : insert, delete : O(1)
    2. 단점 : search : O(n)
    3. 예) 더미 더블링크드 리스트
  3. 만약에 쓴다고 하면 동적배열을 써야 한다. 정말 배열이 붙어있어서 지역성으로 캐시가 발생할 확률이 높다. 하드웨어의 이점을 받을 수 있다.
  4. 이거 2개를 보완해주는게 BST이다.

BST(이진탐색트리)

BBC3A0EB-8974-4B56-9491-890B65AA0023
  • 이진탐색트리
  • 루트를 기준으로 왼쪽에는 항상 더 작은 자료가 위치하고 오른쪽에는 더 큰 자료가 위치한다.
  • *insert, delete, search 모두 O(log(n))
  • 아무리 많이 해도 트리의 높이(height of tree)만큼만 하면 된다.log(n)
  • 트리에 있는 것은 파이썬에서 key값이라고 볼 수 있다.
    • dictionary이고 a collection of pairs(key, item)
    • 해당 key에 item을 참조하게만 해놓으면 문제해결

BST를 공부하기 위해서 기본적으로 Tree에 대해서 알아야 한다. Tree의 search, delete, insert에 대해서 공부해야 한다.

redblack tree

참고 : RedBlack Tree에 대해

스크린샷 2019-07-13 오전 9 20 52
  • 7,6,5,4,3,2,1 순서대로 삽입해서 이진탐색 트리(Binary Search Tree)를 만들어 보면 위의 그림과 같이 한쪽으로 치우처진 tree가 완성된다.

1을 찾으려고 한다면?

  • 항상 트리의 높이만큼 시간이 필요하다. 그렇다면 이진탐색 트리(Binary Search Tree)를 사용하는 이유가 없다.
  • 이러한 문제점을 해결하기 위해 나온 자료구조가 Balanced binary search tree의 한 종류인 RedBlack Tree이다.
스크린샷 2019-07-13 오전 9 23 11
  • 각 노드에 색깔을 저장하는 공간을 추가하여 색깔을 기준으로 균형을 맞추는 트리이다.

자세한 사항은 또 뒤에서 redblack tree만 다루어보자.

B TREE

참고 : B-Tree 개념 정리 | Jlog

  • 기존에 tree에서 할 수 없었던 캐시를(지역성) 추가하여 보완
스크린샷 2019-07-13 오전 9 25 41
  • 한 개의 노드에 여러개의 데이터가 들어갈 수 있다.
  • 지역성 문제를 해결하여 훨씬 효율을 높였다.

자세한 사항은 B tree만 다룬다.

추가

분할 상환 분석(Amortized analysis)

  • 등장배경: 우리가 짜는 함수의 경우는 빅오를 구하기에 굉장히 애매한 경우가 많다. 따라서 대략적으로 빅오를 구하는 방법
  • DA(동적배열)에서 append를 할때 메모리(capacity)가 꽉 찬 경우
  • 메모리(heap)을 돌아다니면서 가능한 공간을 찾아다닌다. 가능한 위치를 찾아서 복사해서 넣게 되면 최소한 O(n)
  • heap이 linked list로 구성되어 있다.
  • 해결방안
    • 우리가 아는 데이터의 갯수가 200개이면 키자마자 250개를 잡아 놓는다.
      • 만약에 3개만 쓰게 되면 굉장히 비효율적이다.
    • 한 개씩 늘리면서 모든 공간이 다 차게 되면 크기를 X2 한다.
  • 가끔 한번씩 O(n)이 필요한데 그만큼 하고 나몬 지속적으로 O(1)이 되므로 이정도는 그렇게 치자.
  • ex) 데이터의 갯수가 3개이면 4개면 된다.
  • 이렇게 하면 캐시히트와 인덱싱을 유지할 수 있다.
  • heap안에 찾아다니면서 2배 크기가 가능한 곳을 찾아다니는 것이다.

2019.07.04 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 정렬 O(nlogn) - 자료의 구조가 많아 질수록 정렬의 효율이 높아진다.

  2. 퀵소트

  3. 머지소트

1. 퀵소트

A32D87E5-9EB4-452C-8050-9B417056689A
  1. 하나의 스택프레임 안에서 conquer를 한 이후에 다시 호출 할 때 divide가 되는 것이다.
  2. 파티션을 알고 있으면 좋다.
    1. 피벗을 기준으로 왼쪽에는 작은 값이, 큰 값은 오른쪽에 오도록 몰아버리는 것
    2. conquer이다.
  3. pivot은 인덱스가 아니라 값이다.(처음 혹은 끝, 중간)
  4. 재귀 함수 사용

def quick_sort(arr, start, end):
    #기저조건(base case)
    if start >= end:
        return

    left = start
    right = end
    pivot = arr[(left+right)//2] #정수형 나누기 해야됨

    #파티션
    #left와 right가 교차하기 전이라면 파티션을 계속 수행한다.
    while left <= right:
        #left가 언제 멈춰야 하나?
        while arr[left]<pivot:
            left+=1
        #right가 언제 멈춰야 하나?
        while arr[right]>pivot:
            right-=1

        # left와 right가 교차하지 않았다면 교환
        if left <= right:
            arr[left], arr[right] = arr[right], arr[left]
            left+=1
            right-=1


    quick_sort(arr, start, right)
    quick_sort(arr, left, end)


if __name__ == "__main__":
    arr = [7, 2, 5, 12, 6, 10, 8, 1, 3]
    start = 0
    end = 7
    quick_sort(arr, start, len(arr)-1)
    print(arr)

2. 머지소트

1C39EE46-73A2-4C49-97B1-05CABD106754
  • 먼저 다 나누고 정렬을 한다.
  • 하나 하나가 다 스택프레임으로 쌓인다.
  • 다 나눈 이후에 왼쪽은 left 오른쪽은 right
def merge(arr, start, mid, end):
    left = start
    right = mid + 1
    temp = []  #로컬 베리어블이라서 업데이트 안하면 사라져버린다.


    # 둘 중에 하나라도 범위를 벗어나기 전가지
    while left<= mid and right <= end:
        if arr[left] <= arr[right]:
            temp.append(arr[left])
            left += 1
        else:
            temp.append(arr[right])
            right += 1

    #만약에 남아있는 것이 right라면
    #right를 돌면서 temp에에 넣는다.

    while right <= end:
        temp.append(arr[right])
        right +=1

    while left <= mid:
        temp.append(arr[left])
        left += 1

    # arr에 temp를 업데이트 하는 코드
    arr[start:end+1] = temp
    print(temp)


def merge_sort(arr, start, end):
    #base_case
    if start >= end:
        return

    mid = (start+end)//2

    #divide
    merge_sort(arr, start, mid)
    merge_sort(arr, mid+1, end)

    #conquer
    merge(arr, start, mid, end)


if __name__ == "__main__":
    arr = [3,6,9,1,3,5,7,8]
    start = 0
    end = len(arr)-1
    merge_sort(arr, start, end)
    print(str(arr))

2019.07.03 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 정렬 O(n2) - 그나마 제일 좋은것은 insertion이 제일 좋다.

  2. 버블솔트

  3. 삽입정렬

  4. 선택정렬

1. 버블 솔트

BF4CA13D-C508-4044-B31B-D6098C0CBFD4
  • 일반적으로 for문이 2개면 n2이다.

def bubble_sort(arr):
    n = len(arr)
    for i in range(n-1):
        for j in range(n-1-i):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]


if __name__ == "__main__":
    arr = [7, 2, 5, 12, 6]
    bubble_sort(arr)
    print(arr)

2. 삽입 정렬

스크린샷 2019-07-01 오후 2 35 21
  • [5, 2, 6, 1, 8]
  • 첫 번째 인덱스는 지나가고 2번 째 인덱스부터 temp에 넣어서 그 숫자를 첫 번째 인덱스부터 비교한다.
  • i를 temp에 넣고, j는 i-1부터 인덱스를 줄여가며 temp와 비교한다. i의 앞쪽은 정렬되어 있을 것이기 때문에 그 해당 위치에 insert를 하는 것이기 때문에 insertion이다.
  • insertion은 끝까지 다 비교하지 않아도 되기 때문에 bubble_sort보다 더 효율적이다.

while문을 통한 구현

def insertion_sort(arr):
    n = len(arr)
    for i in range(1, n):
        temp = arr[i]
        j = i - 1
        while j!=-1:
            if arr[j] > temp:
                arr[j+1] = arr[j]
                j-=1
            else:
                break
        arr[j+1] = temp

if __name__ == "__main__":
    arr = [7, 2, 5, 12, 6]
    insertion_sort(arr)
    print(arr)    

for문을 통한 구현

def insertion_sort(li):
    n=len(li)
    temp=None
    for i in range(1, n):
        temp=li[i]
        for j in range(i-1, -2, -1): #-2까지 해야지 -1까지 된다.
            if j==-1:
                break
            if li[j] > temp:
                li[j+1]=li[j]
            else:
                break
        li[j+1]=temp

if __name__ == "__main__":
    arr = [7, 2, 5, 12, 6]
    insertion_sort(arr)
    print(arr)    

선택 정렬(selection)

0D595E6A-C6A6-41FA-98A1-8E91D148873E
  • 첫 번째 인덱스에 적합한 가장 작은 값을 선택한다.
  • 그리고 해당 인덱스를 첫 번째 인덱스와 바꾼다.
  • i를 0부터 시작해서 일단 첫 번째 인덱스를 가장 작은 값으로 가정하고 시작
  • 바깥 포문은 제일 마지막꺼 전 까지만 돌면 되므로 n-2까지
  • 안 쪽 포문은 i+1부터 n-1(끝까지)
def selection_sort(arr):
    n = len(arr)

    for i in range(0, n-1):
        min_idx = i
        for j in range(i+1, n):
            if arr[j] < arr[min_idx]:
                min_idx = j
        arr[i], arr[min_idx] = arr[min_idx], arr[i]

if __name__ == "__main__":
    arr = [7, 2, 5, 12, 6]
    selection_sort(arr)
    print(arr)

2019.07.02 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 원형규

스크린샷 2019-07-03 오후 5 24 12
  1. circular queue(원형큐)
    1. 크기가 정해져 있는 배열!! or linked list로도 만들 수 있다.
    2. head, tail
    3. 배열, head, tail 만 있으면 구현할 수 있다.
  2. 구현 특징
    1. 핵심포인트 : 텅빈 큐를 어떻게 표현할 것이냐?/ tail이 인덱스를 벗어낫을 떄 어떻게 처리할 것인가?
    2. 텅빈 큐는 head == tail 이면 비었다고 표현한다.
    3. full은 한 칸을 버리지만 tail + 1을 했을 때 h면 full로 표현한다.
    4. tail은 마지막 데이터의 다음을 가르키게 구현을 한다.
  3. 큐사이즈는 정해주거나, 작은 숫자를 바탕으로 테스트하면 좋다.

class CQueue:
    #모든 인스턴스가 필요하면 클래스 멤버로 선언

    MAXSIZE = 10

    def __init__(self):
        #리스트 컨프렌션
        self.__container = [None for _ in range(CQueue.MAXSIZE)]
        #인포메이션 하이딩
        self.__head = 0
        self.__tail = 0

    def is_empty(self):
        if self.__head == self.__tail:
            return True
        return False

    def is_full(self):
        next = self.__step_forward(self.__tail)
        if next == self.__head:
            return True
        return False

    def enqueue(self, data):
        if self.is_full():
            raise Exception("The queue is full")

        self.__container[self.__tail] = data
        #tail은 마지막 데이터의 다음을 가르킨다.
        self.__tail = self.__step_forward(self.__tail)

    def dequeue(self):
        if self.is_empty():
            raise Exception("The queue is empty")

        result = self.__container[self.__head]
        self.__head=self.__step_forward(self.__head)
        return result

    def peek(self):
        if self.is_empty():
            raise Exception("The queue is empty")
        return self.__container[self.__head]

    # 편의함수
    def __step_forward(self, x):
        x+=1
        if x >= CQueue.MAXSIZE:
            x = 0
        return x

if __name__ == "__main__":
    cq = CQueue()

    for i in range(5):
        cq.enqueue(i)

    while not cq.is_empty():
        print(cq.dequeue(), end = "   ")

2019.04.12 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 자료구조

자료구조에 대한 정의는 너무나도 많다.
그 중에 가장 마음에 와닿는 글이 있어서 정리를 해본다.

" 프로그래밍은 결국 데이터를 다루는 방법입니다. 데이터를 입력받고, 데이터를 처리하여 도출된 데이터를 출력합니다.

자료구조는 데이터를 입력 받아 어떻게 저장하는 지에 대한 학문입니다.
데이터를 어떻게 저장하는지(입력), 어떻게 찾는지(탐색), 어떻게 지우는지(삭제)가
자료구조의 종류에 따라 천차만별입니다.

그럼, 어떤 자료구조를 언제 사용해야 할까요? 이는 결국 성능과 효율적인 메모리 사용에 달려있습니다.
프로그래머가 처한 상황에서 가장 뛰어난 성능을 내고 또 가장 효율적으로 메모를 사용한다면 그 자료구조를 선택하면 됩니다.

알고리즘은 방법론입니다. 많은 알고리즘이 있지만, 자료구조에 연관된 알고리즘만 언급하겠습니다.
자료구조와 알고리즘은 매우 밀접한 연관을 가지고 있습니다. 위에 언급한 삽입 정렬과 병합 정렬은 엄밀히 말해 알고리즘입니다. 하지만 자료구조를 배울 때 반드시 마주치게 되지요. 알고리즘은 자료구조를 구현하는 방법이라고 보시면 될 것 같습니다. "

“프로그래머가 갖추어야 할 ‘기본’은 무엇인가?” 넥슨 개발자 컨퍼런스(NDC) 참관 후기 | 패스트캠퍼스

자료구조의 구성

  1. insert : 어떻게 데이터를 넣을 것인가?
  2. search : 어떻게 데이터를 찾을 것인가?
  3. delete : 어떻게 데이터를 삭제할 것인가?

자료구조의 분류

자료구조
  • 단순구조 : 프로그래밍에서 사용되는 기본 데이터 타입
  • 선형구조 : 저장되는 자료의 전후관계가 1:1 (리스트, 스택, 큐 등)
  • 비선형구조 : 데이터 항목 사이의 관계가 1:n 또는 n:m (트리, 그래프 등)
  • 파일구조 : 서로 관련된 필드들로 구성된 레코드의 집합인 파일에 대한 자료구조
  • 출처 : 강의노트 17. 알고리즘, 자료구조 개요 · 초보몽키의 개발공부로그

선형구조(배열과 linked list 그리고 스택과 큐)

  1. 배열(array)
    1. 같은 자료형의 변수를 모아 놓은 것
    2. 인덱싱 활용 가능(search)
      1. 검색으로 배열을 이길 수는 없다.
    3. 데이터가 군집화 되어있다.
      1. 캐시히트의 가능성이 높다.
    4. 검색속도가 그 어떤 자료구조보다 빠르다.O(1)
    5. 데이터의 삽입과 삭제가 느리다. 최악의 경우 O(n)
      1. 맨 앞에 데이터를 삽입하게 되면 모든 데이터를 복사해서 한칸씩 다 옴겨야 한다.
      2. 배열은 공백을 인정하지 않는다. O(n)
  2. Linked list ( 데이터의 삽입과 삭제가 많고, 검색이 별로 없을 때)
    1. 검색 속도 최악 O(n)
    2. 데이터의 삽입과 삭제는 O(1)
      1. 어디든 데이터를 놓고 연결만 해주면 된다.
    3. 데이터가 흩어져 있다.
      1. 캐시히트의 가능성이 굉장히 떨어진다. -> 캐시미스 발생
      2. 페이지폴트까지 날수도 있다.
    4. 미리 힙을 주고 흩어져 있는 문제를 해결할 수 있다.
      1. 다이나믹 힙
  3. Stack
    1. 무언가를 쌓는다라는 의미를 가진 자료구조이다.
    2. LIFO(Last in, First out) : 후입선출
  4. Queue
    1. 우리의 일반적인 줄서기
    2. FIFO(First in, First out) : 선입선출

만약에 배열과 Linked list 2개 다 사용가능한 상황이면 무조건 배열을 쓴다.
배열은 메모리의 스택에 할당해서 사용한다.

파이썬에서의 list

파이썬의 list는 배열과 다르다.
파이썬은 포인터 배열이다.
포인터는 배열처럼 일렬로 되어 있으나, [1,2,3]
1은 class int 여서 상수객체이기 때문에 4바이트보다 훨씬 크다.

파이썬의 리스트는 파이썬에서는 같은 자료형의 변수를 모아 놓은 것이 아니다.
파이썬에는 배열이라는 자료구조가 없는 것이다. 즉 지원하지 않는다.

예시

얕은 복사

>>>    #얕은 복사
>>>    li = [1,2,3,4]
>>>    li3 = li.copy() # or copy.copy(li)도 가능
>>>    li3     #[1,2,3,4]
>>>    li3.append(5)
>>>    li3      #[1,2,3,4,5]
>>>    li       #[1,2,3,4]
>>>

위의 내용을 이해하기 위해서는 단순히 객체가 [1,2,3,4]의 형태처럼 메모리에 올라가 있다고 생각하면 안된다.
실제적으로 파이썬에서 list를 생성한다고 하더라도 모두 모여서 메모리에 올라가지 않는다. [* , * , * , * ]와 같은 형태로 생성되어 첫번째 * 은 1을 가르키고 두 번째 * 은 2를 가르키고 이런 형식이다. 얕은 복사는 [* , * , * , * ]를 복사해 온 것이다. 따라서 li3는 li의 [* , * , * , * ]를 복사해온 것이다. 따라서 li3에서 append를 통해 새로운 요소를 추가하게 되면 li3의 [* , * , * , * ]가 [* , * , * , * , * ]가 되어서 마지막 * 가 새로운 요소를 가르키게 된다. 따라서 li3가 변경되더라도 li는 변경되지 않는다.
하지만 여기에서도 예외는 있다.

>>>    #얕은 복사
>>>    li = [1,2,3,[4,5]]
>>>    li3 = li.copy()
>>>    li3    #[1,2,3,[4,5]]
>>>    li3[3].append(6)
>>>    li3    # [1,2,3,[4,5,6]]
>>>    li     # [1,2,3,[4,5,6]]
>>>

위에서 보면 알 수 있듯이 원래 얕은 복사에서라면 li3를 바꾼다고 해서 li가 바뀌어서는 안된다. 하지만 li는 변경되었다. 그렇다면 li3는 왜 변경되었을까? 위에 언급한대로 li3는 li의 [* , * , * , * ]를 복사해 온 것이다. 위에 예시에서 마지막 * 은 3번째 인덱스 [4,5]을 가르키고 있다. li3와 li 모두 [4,5]을 가르키고 있는 것이다. 따라서 li3를 통해 [4,5]를 변경하게 되면 마지막 *가 가르키는 것이 변경되는 것이므로 li 역시 변경되게 된다.(즉 li와 li3의 내부리스트는 각은 객체를 참조하기 떄문이다.)

이러한 것을 맡기 위해서는 깊은 복사가 필요하다.

깊은 복사

>>>    import copy     #deepcopy를 위해서는 copy를 import 해주어야 한다.
>>>    li = [1,2,3,4]
>>>    li4 = copy.deepcopy(li)
>>>    li4        # [1,2,3,4]
>>>    li4.append(5)
>>>    li4        # [1,2,3,4,5]
>>>    li         # [1,2,3,4]
>>>    li = [1,2,3,[4,5]]
>>>    li4 = copy.deepcopy(li)
>>>    li4        # [1,2,3,[4,5]]
>>>    li4[3].append(6)
>>>    li4        # [1,2,3,[4,5,6]]
>>>    li         # [1,2,3,[4,5]]

깊은 복사를 통해서는 완전히 동일한 새로운 객체를 생성하는 것이므로 li4에 어떤 요소를 추가해도 li는 전혀 영향을 받지 않는다. 따라서 상황에 맡게 단순 객체 복사, 얕은 복사, 깊은 복사를 사용해야 한다.

참고 : 2019_03_23_TIL(깊은 복사 얕은 복사)

비선형구조

트리

  • connected acyclic graph
    • 사이클(순환)이 없는 연결된 그래프
    • 루트노드(root)를 반드시 가진다.
    • 트리를 구성하는 노드 간에 단순 경로가 반드시 존재한다.
      • 단순경로란 지나왔던 접점을 다시 지나지 않는 경로
      • 한 개의 노드에서 다른 한개의 노드를 선택하면 단순 경로가 항상 존재
    • 루트 노드를 제외한 나버지 노드들은 분리집합으로 분할이 가능하며 이 집합들은 각각 하나의 트리를 구성한다(재귀적 정의)

트리 용어 정리

자료구조 용어 정리

부모노드와 왼쪽 자식노드 , 오른쪽 자식노드로 나누어짐

  • 루트노드 : 뒤로 뒤집어서 보면 여기서부터 쭉 퍼져나가므로 root(뿌리) 노드이다. 트리의 시작점
  • 에지 : 노드를 연결하는 선
  • 리프노드 : 자식노드가 하나도 없는 노드
  • 인터널노드 : 자식노드가 하나라도 있는 노드
  • 차수 : 자식노드의 갯수
  • 트리의 차수 : 트리에 있는 노드들 중 최대 차수

이진 트리

이진트리
  • 어떤 노드의 자식 노드의 수가 최대 2개인 트리

이진 트리의 종류

C9E60ED9-D5EA-4E44-9157-265B242B2B4F
  • 포화 이진 트리
    • 모든 레벨이 꽉 차 있다.
    • 즉 모든 노드들의 2개의 자식노드를 가지고 있다.(리프노드 제외)
4E72888E-46D9-46B1-95B6-BE894FD21905
  • 완전 이진 트리
    • 트리의 노드가 위에서 아래로, 왼쪽에서 오른쪽으로 채워지는 트리
    • 가장 높은 레벨 단계에서 항상 왼쪽에서 오른쪽으로 채워져 있어야 한다.

트리의 순회는 다음 장에서 추가로 공부한다!

  • 순회란 트리의 모든 노드를 중복하지 않으면서 방문하는 것을 말하며, 데이터를 찾고, 저장하고, 삭제하는데 쓰인다.

2019.04.09 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 데이터 베이스

mysql은 관계형 데이터 베이스이다.
관계형 데이터 베이스 시스템들은 SQL 문법을 사용한다.

  • nosql : 빅데이터와 같이 큰 데이터가 늘어나면서 관계형 데이터가 한계가 있다고 생각

DATABASE SERVER

server이기 때문에 앞으로 들어나지 않는다.
database server밑에서 여러개의 database가 있을 수 있고
database 밑에서는 여러개의 table이 있다.
그 안에 DATABASE가 있다.

database

  1. oracle이 1위 회사이다.
  2. 중소기업은 mysql —> php와 너무 잘 맞다. APM(apach, php, mysql)
  3. MariaDB
    django
  4. postgreSQL —> open source

database는 table이라는 것을 카테고라이징 한 것이다.

  • table은 entity 혹은 relation이라고 부른다.
  • 열은 column 혹은 field라고 부른다.
  • 행은 row 혹은 tuple이라고 부른다.(파이썬의 자료형 tuple과 동일하다)
  • 또한 이 row 한 줄을 recode라고 한다.
  • 레코드는 구체적인 데이터를 이야기한다.

기본적으로 행과 열을 통해서 데이터베이스에 접근한다.

DATABASE CLIENT

mysql-client
phpmyadmin
mysql query browser
navicat

  • 참고사항
  • utf-8 mb4 (unicode utf-8)의 의미
    • a,b,c —> 1byte
    • 한글 —> 3byte
    • 어차피 3바이트까지 하면 되는데 굳이 4바이트까지 써야하나?
    • 따라서 자체적으로 3바이트로 만들었는데
    • 이모티콘이 나오면서 4바이트로 바꿈(utf-8 mb4)

시작하기

students

  • student ID - int, primary key(테이블에서 오직 하나)
  • height
  • score
  • birthday
  • class ID - int, NULL

teacher

  • teacher - int, primary key
  • subject - varchar(가변)/ not null
  • class id - int, NULL

server start

mysql.server start
mysql -u root

  • mysql -u root ===> connection이 연결 TCP 연결(socket으로 연결이 둟린 것이다.)
  • 이것을 session이라고 한다. TCP 연결을 했다.
  • client는 여려명일 수 있다. john, greg 등
  • 쓰레드의 가장 큰 문제점 : 레이스 컨디션
    • 여러명이 접속하게 되면 table이 shared resource가 된다. (공유자원)
    • updata문을 통해서 balance 값을 5000원에서 3000원으로 바꾸면서 끊김
    • 무결점을 보장해줘야한다.
    • 그렇기 때문에 session을 연 다음에 update를 하는 순간 그 쓰레드에 대해서만 복사해서 준다.
    • 트랜젝션을 업데이트를 한 다음에 commit을 통해 트랜젝션을 끝내고 데이터 베이스를 바꿈.
    • commit전에는 절대 데이터 베이스는 바뀌지 않는다. 무결성
    • 트랜젝션과 세션의 차이를 잘 알아야 한다.
    • Do it! 오라클로 배우는 데이터베이스 입문 - 이지훈 - Google 도서

유저확인

SELECT USER, HOST FROM USERS;


유저 생성 및 비밀번호 설정

CREATE USER “byeonguk”@“localhost” IDENTIFIED BY "1234";


권한 주기

GRANT ALL PRIVILEGES ON *.* TO "byeonguk"@"localhost" with grant option;


권한 적용

FLUSH privileges


권한 확인

SELECT USER, HOST, SUPER_PRIV FROM MySQL.USER;


데이터 삭제 혹은 테이블 삭제

DROP DATABASE mydb;
DROP TABLE 테이블명


테이블에 대한 정보 확인

SHOW tables

  • 테이블 리스트 확인
    SHOW CREATE table "테이블 명"
  • 해당 테이블에 대한 정보
    DESC "테이블 명";
  • 해당 테이블 스키마 열람
  • 스키마란 : 테이블에 적재될 데이터의 구조와 형식을 정의 하는 것
    SELECT * FROM "테이블명"
  • 우리가 찾던 그 정보확인

테이블 생성

CREATE TABLE students(
-> student ID INT AUTO_INCREMENT, 
-> studentName VARCHAR(20)  #학생의 이름이 20바이트까지 가능
-> height SMALLINT DEFAULT 200,
-> score, SMALLINT NULL,
-> birtyday DATE NOT NULL,
-> classID INT NULL,
-> PRIMARY KEY(studentID),                    # 오직 한개만 있다
-> FOREIGN KEY(classID), REFERENCES classes(classID) 
  • classID라는 외래키 참조하는데 classes 열에서 classID를 참조한다.
  • 잘못되었을 때 \c를 하면 빠져나올 수 있다.
CREATE TABLE teachers (
->teacherID INT AUTO_INCREMENT NOT NULL,
->subject VARCHAR(30) UNIQUE,
->classID INT NULL,
->PRIMARY KEY(teacherID),
->CONSTRAINT fk_classID
->FOREIGN KEY (clastsID) REFERENCES classes (classID)
);

데이터 삽입

INSERT INTO students 
-> (studentName, height, scores, birthday, classID)
-> VALUES
-> (‘GREG’, 180, 87, ‘2002-3-23’, 1)

칼럼(열)더하기

DESC teacher;        #직관적으로 현 상황을 볼 수 있음

ALTER TABLE teachers
-> ADD COLUMN teacherName ;

SHOW CREATE TABLE teacher;          #해당 테이블에 대한 정보 제공(create할 때의 정보)

백업하기

mysqldump -u root -p —databases 
mydb > mydb1.sql
#mydb1.sql이 생김

카피하기

CREATE TABLE student_cp
-> (SELECT * FROM students); 

SELECT * FROM student_cp  #잘 복사되었는지 확인

데이터 업데이트하기

UPDATA student_cp
-> SET score = 100
-> WHERE studentName = ‘Mary’;             #Mary를 해주지 않으면 전부 다 100으로

메리 점수만 확인하기

SELECT studentName, score FROM student_cp
->WHERE studentName like ‘Mary’;
# WHERE studentName like "Ma%"; 
# %는 어떤 글자든지 다 소화시켜준다.

데이터 베이스에 조건문 넣기 (50점 이상 70점 이하 점수 보기)

SELECT studentName, score
-> FROM student_cp
-> WHERE score > 50 and score < 70

특정 데이터 지우기

Delete from student_cp
-> WHERE studentName like “Mary’;

SELECT * FROM student_cp (확인)

foreinkey 없애기

foreinkey를 없애려면
자동으로 인덱스를 잡기 때문에

폴인키 제약조건을 없앤 다음에 쇼 인덱스를 해준 다음에 잡혀있는 인덱스를 없애준다.

1)
ALTER TABLE teachers
-> DROP FOREIGN KEY teachers;

2)
DROP INDEX classID
-> ON teachers;

SHOW CREATE TABLE teachers      #확인

JOIN

innerouter

join은 테이블을 가로로 연결할 때 쓴다.

  • inner join
  • outer join
    • left outer join
    • right outer join
    • full outer join
      • mysql, mariadb는 full outer을 지원하지 않는다.
      • 필요할 때 union을 통해 구현한다.

MySQL full outer join : 네이버 블로그


INNER JOIN

ex) join 예시
student name / score / class id / teacher name / subject
==> class id로 묶는다.( class id에 대한 교집합)

NULL을 어떻게 처리할 것인가?

  • inner은 빠진다.

if select studentname, classid, teacher 이렇게 하면 classid가 누구것인지 알수가 없다.

S.를 붙여 student 표현 T.를 붙여 teacher 을 표현

  • 적용 방법
    • SELECT S.studentsName,S.score, S.classID, T.teacherName, T.subject
    • FROM students S INNER JOIN teachers T (student가 left teacher가 right)
    • ON S.classID = T.classID
    • ORDER BY S.classID

LEFT OUTER JOIN ( 왼쪽에 위치한 집합은 다 나온다.)

SELECT S.studentName, S.score, S.classID,
    -> T.teacherName, T.subject
    -> FROM students S LEFT OUTER JOIN teachers T
    -> ON S.classID = T.classID

RIGHT OUTER JOIN (오른쪽에 위치한 집합은 다 나온다.)

SELECT S.studentName, S.score, S.classID
    -> T.teacherName, T.subject
    -> FROM students S RIGHT OUTER JOIN teachers T
    -> ON S.classID = T.classID;

FULL OUTER JOIN

SELECT S.studentName, S.score, S.classID
    -> T.teacherName, T.subject
    -> FROM students S LEFT OUTER JOIN teachers T
    -> ON S.classID = T.classID
    -> UNION
    -> SELECT S.studentName, S.score, S.classID
    -> T.teacherName, T.subject
    -> FROM students S RIGHT OUTER JOIN teachers T
    -> ON S.classID = T.classID;

GROUP BY

  • 전체 평균
    • SELECT AVG(score) FROM students;
  • 반별로 평균을 알고 싶다.
    • SELECT classID, AVG(score)
    • -> FROM students
    • -> GROUP BY classID
  • 만약에 반 배정 안 받는 애들은 빼고 싶다.
    • SELECT classID, AVG(score)
    • -> FROM students
    • -> WHERE classID IS NOT NULL
    • -> GROUP BY classID;
  • 평균 65점 이상인 반을 본다.
    • SELECT classID, AVG(score)
    • -> FROM students
    • -> WHERE classID IS NOT NULL
    • -> GROUP BY classID
    • -> HAVING AVG(scores) > 65;

WHERE 와 GROUP BY, HAVING

WHERE와 ==> 그룹을 하기 전에 조건절 수행
GROUP BY
HAVING의 차이 ==> 이미 그룹바이로 그룹이 끝난 이후에 조건절 수행(반듯이 그룹을 한 것에 대해서만 조건절을 수행한다)


M : N

  • INNER JOIN을 2개 사용한다.
    ex) Student와 subject와의 관계

  • VARCHAR 와 CHAR의 차이 : 비트를 고정해줄 것인가? 아니면 유동적으로 할 것이냐?

CREATE TABLE subjects(
    -> subjectName CHAR(20) UNIQUE NOT NULL,     #unique not null은 프라이머리 key가 없으면 생성한다.
    -> roomNum TINYINT NOT NULL);
INSERT INTO subjects 
(subjectName, roomNum) 
VALUES ('math', 101), ('literature', 105), 
('science', 107), ('english', 110), ('ethics', 111);

학생 입장에서 subject name + 강의실 번호까지

sql> SELECT ST.studentName, ST.score, 
SB.subjectName, SB.roomNum
FROM students ST INNER JOIN student_subject SS
ON ST.studentName=SS.studentName
INNER JOIN subjects SB
ON SS.subjectName=SB.subjectName
ORDER BY ST.studentName;

과목 입장에서 배열

sql> SELECT SB.subjectName, SB.roomNum, 
ST.studentName, ST.score 
FROM students ST INNER JOIN student_subject SS 
ON ST.studentName=SS.studentName 
INNER JOIN subjects SB 
ON SS.subjectName=SB.subjectName 
ORDER BY SB.subjectName;

SELECT의 순서가 바뀌는 것은 크게 의미가 없고
마지막에 ORDER BY에 따라서 정렬 순서를 정해준다.


SQL

  • VIEW
  • INDEX
    • CLUSTERED INDEX
    • SECONDARY INDEX

VIEW

VIEW는 select문이다.
view를 마치 테이블처럼 사용한다.

  1. view는 select문일 뿐이지 테이블이 새로 만들어지는 것이 아니다.
  2. 가독성이 높아진다.
  3. 보안문제 해결

ex) 쇼핑몰
customer / 생년 월일 /

  • 알바생에서 customer 테이블을 그대로 보여주면 안되니깐 view(생년월일이 없는)를 하나 만들고 권한을 부여(GRANT)

CREATE VIEW

CREATE VIEW view_st_sb_join 
AS 
SELECT ST.studentName, ST.score,
SB.subjectName, SB.roomNum  
FROM students ST INNER JOIN student_subject SS 
ON ST.studentName=SS.studentName 
INNER JOIN subjects SB 
ON SS.subjectName=SB.subjectName 
ORDER BY ST.studentName;

WHERE 적용 가능

SELECT * FROM view_st_sb_join;

sql> SELECT * FROM view_st_sb_join 
WHERE score BETWEEN 50 AND 70;

DROP VIEW 적용 가능

DROP VIEW view_st_sb_join;

INDEX

index를 쓰는 이유 <-- 데이터 검색

Tree BST --> o(log n)
단점 보완 -> red - black - tree (균형 이진 트리) 추가 단점 보완 => B-tree

  • CLUSTERED INDEX

    • 한 테이블마다 1개씩만 존재한다.
    • 데이터 자체를 클러스트 인덱스에 맞추어서 정렬한다.
    • primary key가 클러스트 인덱스가 된다.
    • primary key를 안 잡고 데이터를 넣은 뒤 primary key를 잡으면 나중에 굉장히 정렬하는 시간이 오래 걸릴 수 있다. ==> 항상 미리 잡고 시작하자.
  • SECONDARY INDEX

    • 원하는만큼 붙일 수 있다.
    • B-트리라는 자료구조를 만들고 실제 데이터를 참조해서 만든다.
      • where 에 쓰인다. ex) where classID = 3
      • secondary index를 언제만드냐?
        • where가 자주 쓰일 때 만들어 놓으면 search를 할 때 secondary index를 활용하여 서치
        • 쓰는 이유 : 검색 속도가 엄청나게 빨라진다.
        • 단점 : 테이블에 대해 insert를 계속하고 업데이트를 계속한다고 하면 계속 B-tree도 업데이트 시켜줘야하고, 수정, 삽입 등이 많이 늘어나면 페이지 분할이 일어나서 속도가 늦어질 수 있다.
        • where을 내가 얼마나 쓸 것인지 파악하고, 내가 수정 삽입 등을 얼마나 할 것인지 고민
    • CREATE INDEX는 secondary index만 만들 수 있다.
    • cluster int를 만들기 위해서는 ALTER TABLE이라는 커맨드를 써야한다.

참고사항

내부 스키마 외부 스키마 개념 스키마
[DB기초] 스키마란 무엇인가?

2019.04.01 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 자료구조의 큰 틀에 대해 이야기하기

자료구조

자료구조는 메모리에 data를 어떻게 저장할 것인가에 대한 고민부터 시작된다.
list, tuple, dictionary 모두 자료구조의 한 형태이다

자료구조는 딱 3가지이다.

  1. 데이터를 어떻게 삽입할 것인가?(insert)
  2. 데이터를 어떻게 검색할 것인가?(search)
  3. 데이터를 어떻게 삭제할 것인가?(delete)

ADT(abstract data type) : 추상 자료형

자료 구조에서 삽입, 탐색, 삭제 등을 담당하는 함수들의 사용 설명서

  1. 어떤 자료구조가 가지고 있는 오퍼레이션(함수)의 나열(목록)
  2. 함수 시그니처(인터페이스)만 나열할 뿐, 내부 구현은 표기하지 않음
  3. 함수(operation)의 작동 방식 설명.

ex)

>>>    help(list.append)
help on method_descriptor:

append(...)
    L.append(object) -> None
>>>

우리는 여기에서 append함수가 어떻게 구현되었는지 알 수 없다. 하지만 append함수를 사용하는 것에는 전혀 문제가 없다. 이러한 특징을 인터페이스와 구현이 분리되었다고 하며 추상화라는 표현을 쓴다.

linked List

데이터와 참조로 구성된 노드가 한 방향 혹은 양방향으로 쭉 이어져 있는 자료구조

  • single linked list
  • double linked list

노드

자료구조를 구현할 때 데이터를 담는 틀

node
  • 노드는 저장할 데이터와 다음 노드를 가르키는 참조로 이루어져 있다.(단일 연결리스트)
  • double linked list에서는 다른 형태의 노드가 활용된다.

single linked list

single linked list에서의 노드 구현

class Node:
    def __init__(self, data=None):
        self.__data = data
        self.__next = None


# 소멸자
# 객체가 사라지기 전에 반드시 한번 호출하는 것을 보장한다.
# 소멸자를 호출하고 지우는 것을 보장해준다.
# 실제 로드가 사라지는 것을 확인하기 위해서 가지고 옴
    def __del__(self):
        print(f'node[{self.__data}] deleted!')
        pass

    @property
    def data(self): 
        return self.__data

    @data.setter
    def data(self, data):
        self.__data = data

    @property
    def next(self):
        return self.__next

    @link.setter
    def next(self, next):
        self.__next = next
  • 자료구조에서는 보안이 중요하므로 정보은닉 2가지를 모두 적용해주었다.
  • 멤버 __data에는 데이터를 저장하고, 멤버 __ next는 다음 노드를 가르킨다.

single linked list의 인스턴스 멤버

  1. head -> 리스트의 첫 번째 노드를 가르킨다.
  2. d_size -> 리스트의 요소 개수이다.

single linked list의 ADT

  1. S.empty() ---> Boolean

    • 리스트가 비었으면 True, 아니면 False
  2. S.size() ---> integer

    • 리스트에 있는 요소 개수
  3. S.add() ---> None

    • 노드를 리스트의 맨 앞에 추가
  4. S.search(target) ---> node

    • 리스트에서 target을 찾는다.
    • 찾으면 노드를, 못 찾으면 None 반환
  5. S.delete() ---> None

    • 맨 앞의 노드 삭제

single linked list 구현


class SLinkedlist:

    def __init__(self):
        self.head = None
        self.d_size = 0

    def empty(self):
        if self.d_size == 0:
            return True
        else:
            return False

    def size(self):
        return self.d_size

    def add(self, data):
        new_data = Node(data)
        new_data.next = self.head
        self.head = new_data
        self.d_size += 1

    def search(self, target):
        cur = self.head
        while != None:
            if cur.data == target:
                print(f"찾으신 데이터:{cur.data}가 있습니다")
                return cur
            cur = cur.next
        return None

    def delete(self):
        self.head = self.head.next

def show_list(sll):
    cur = sll.head
    for _ in range(sll.size()):
        print(cur.data, end=" ")
        cur = cur.next

if __name__ == "__main__"
    single = SLinkedlist()
    single.add(1)
    single.add(2)
    single.add(3)
    single.add(4)
    single.add(5)

    print(single.size())
    single.search(3)
    show_list(single)

    single.delete()
    show_list(single)

5
찾으신 데이터 3가 있습니다.
1 2 3 4 5
node[5] deleted!
2 3 4 5
node[4] deleted!
node[3] deleted!
node[2] deleted!
node[1] deleted!

Dummy double linked list

  • dummy

    • 더미란 데이터를 가지지 않는 노드를 의미한다.
    • double linked list에서는 양 옆에 배치한다.
  • 구현 이유

    • 더미가 있게 됨으로서 데이터가 있던지 없던지 상관없이 간단하게 데이터를 추가 삭제할 수 있게 되었다.원래는 뒤에 데이터가 있는지 앞에 데이터가 있는지 확인을 해야하지만 더미(head와 tail)이 있게 되므로서 상관하지 않고 구현할 수 있다.

double linked list 노드

노드1

Dummy double linked list instance Member

  • instance member

    • head : 리스트 맨 앞에 있는 더미를 가르킨다.
    • tail : 리스트 맨 뒤에 있는 더미를 가르킨다.
    • d_size : 리스트의 요소 개수
  • ADT

double linked list 1 double linked list 2

Dummy double linked list 구현

class Node:
    def __init__(self, data=None):
        self.__data = data
        self.__prev = None
        self.__next = None
        print(self)

    def __del__(self):
        print("data of {} is deleted".format(self.data))

    @property
    def data(self):
        return self.__data

    @data.setter
    def data(self, data):
        self.__data = data

    @property
    def prev(self):
        return self.__prev

    @prev.setter
    def prev(self, b):
        self.__prev = b

    @property
    def next(self):
        return self.__next

    @next.setter
    def next(self, n):
        self.__next = n

    def __str__(self):
        return f"created:[{self.__data}]"


class DoubleLinkedList:

    def __init__(self):
        self.head = Node()  
        self.tail = Node()
        self.d_size = 0

        self.head.next = self.tail
        self.tail.prev = self.head

    def empty(self):
        if self.d_size == 0:
            return True
        else:
            return False

    def size(self):
        return self.d_size

    def add_first(self, data):  
        new_node = Node(data)  
        new_node.next = self.head.next
        new_node.prev = self.head
        self.head.next.prev = new_node
        self.head.next = new_node
        self.d_size += 1


    def add_last(self, data):  # 항상 제일 앞에는 first가 있다고 가정한다.
        new_node = Node(data)
        new_node.next = self.tail
        new_node.prev = self.tail.prev
        self.tail.prev.next = new_node
        self.tail.prev = new_node
        self.d_size += 1

    def insert_after(self, data, node):
        new_node = Node(data)            
        new_node.next = node.next
        new_node.prev = node
        node.next.prev = new_node
        node.next = new_node
        self.d_size += 1

    def insert_before(self, data, node):
        new_node = Node(data)
        new_node.next = node
        new_node.prev = node.prev
        node.prev.next = new_node
        node.prev = new_node
        self.d_size += 1

    def search_forward(self, target):
        cur = self.head.next
        while cur is not self.tail:
            if cur.data == target:
                return cur
            cur = cur.next
        return None  

    def search_backward(self, target):
        cur = self.tail.prev
        while cur is not self.head:
            if cur.data == target:
                return cur
            cur = cur.prev
        return None


    def delete_first(self):
        if self.empty():
            return
        else:        
            self.head.next = self.head.next.next
            self.head.next.prev = self.head

        self.d_size -= 1

    def delete_last(self):
        if self.d_size == 0:
            return

        self.tail.prev = self.tail.prev.prev
        self.tail.prev.next = self.tail
        self.d_size -= 1

    def delete_node(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

        self.d_size -= 1

    # 편의 함수
    def traverse(self, start=True):
        if start:
            # 리스트의 첫 데이터부터 순회를 시작
            cur = self.head.next
            while cur is not self.tail:  # is는 객체 자체의 비교이다.
                yield cur
                cur = cur.next

        else:
            # 리스트의 마지막 데이터부터 순회
            cur = self.tail.prev
            while cur is not self.head:
                yield cur  # 만약에 아래 쪽에 node.data를 안했으면 cur.data해야 한다.
                cur = cur.prev


def show_list(d_list):
    g = dlist.traverse()
    for node in g:
        print(node.data, end=" ")
    print()


if __name__ == "__main__":
    dlist = DoubleLinkedList()
    dlist.add_first(1)
    dlist.add_first(2)
    dlist.add_first(3)
    dlist.add_first(4)
    dlist.add_first(5)

    show_list(dlist)

    # 데이터를 찾은 이유는 그 뒤에 데이터를 넣기 위해서
    searched_data = dlist.search_backward(3)
    if searched_data:
        # dlist.delete_node(searched_data)
        dlist.insert_before(15, searched_data)
        print(f"searched data:{searched_data.data}")
    else:
        print('기준 노드가 없습니다. 다시 확인해주세요.')

    show_list(dlist)
    # dlist.delete_first()
    # dlist.delete_last()
    del_data = dlist.search_forward(15)
    if del_data:
        # del_data가 15를 가르키고 있으므로 레퍼런스 카운터가 0이 안된다. 따라서 None을 해서 레퍼런스 카운터를 0으로 만든다.
        dlist.delete_node(del_data)
        del_data = None
    else:
        print("기준 노드가 없습니다. 다시 확인해주세요")
    show_list(dlist)
    print("*" * 100)
    # generator 객체


created:[None]
created:[None]
created:[1]
created:[2]
created:[3]
created:[4]
created:[5]
5 4 3 2 1 
created:[15]
searched data:3
5 4 15 3 2 1 
data of 15 is deleted
5 4 3 2 1 
****************************************************************************************************
data of None is deleted
data of 1 is deleted
data of 2 is deleted
data of 3 is deleted
data of 4 is deleted
data of 5 is deleted
data of None is deleted
  • self를 앞에 붙이는 것은 무언가 지속적으로 남겨놓을 때 붙인다.
  • 따라서 지속적으로 남겨놓을 필요가 없을 때는 붙이지 않는다.

+ Recent posts