[TIL 9일자] 데브코스 데이터엔지니어링

·2023년 4월 20일
0

데브코스

목록 보기
8/55
post-thumbnail

📚 오늘 공부한 내용

✔ 특강 (좋은 코드란 무엇인가?)

1. 좋은 코드란?

  • 읽기 쉽고, 이해하기 쉽고, 수정하기 쉬워야 한다.
  • 함수 이름 등의 이름이 명확하고, 의미 있는 주석, 일관된 형식으로 체계적으로 구성되어야 한다.
    -> 잘 모르겠을 때는 chatGPT에게 물어보자.
  • 테스트가 가능하며 코드와 함께 유닛 테스트를 작성해야 한다.
  • 클래스함수는 분명하게 하나의 일을 하도록 구현해야 한다. (하나의 기능)
  • 코드는 모듈화하거나 혹은 루프를 돌려서 중복을 피해야 한다.
  • 오류 처리는 일관성 있게 해야 하고, 디버깅할 때 도움이 될 문제를 로깅을 잘해야 한다.
    -> 이후 운영 관점에서 매우 중요하다.
  • 코드 작성은 내가 아닌 사용자를 염두에 두고 설계한다.

2. 코드 작성 원칙

1) DRY (Don't repeat yourself)

- 같은 코드를 여러 번 작성하고 있다면 내가 중복된 코드를 사용하고 있다는 `시그널`.  
- 한 번 작성하여 필요한 곳에 `재사용할 수 있는 방법`을 찾아야 한다.
- 그러므로 반복된 작업이면 `함수`로, 동일한 일이 반복된다면 `루프`, 비슷한 클래스가 계속 생긴다면 `클래스 계승 구조`를 사용해 볼 것.
#DRY가 적용되지 않은 경우
f_temp = 55
k_temp = (f_temp - 32) * (5/9)

f_temp2 = 49
k_temp2 = (f_temp - 32) * (5/9)

#DRY를 적용한다면
def fahr_to_celsius(fahr):
	celsius = (fahr-32) * (5/9)
 return celsius

k_temp = fahr_to_celsius(55)
k_temp2 = fahr_to_celsius(49)

2) KISS (Keep It Simple, Stupid)

- 하나의 함수가 너무 많은 기능을 가지면 안 된다. 한 함수가 너무 길이가 길어지면 이를 다수의 함수로 나눌 것.
- 불필요한 복잡성을 피하고 코드를 가능한 단순하게 유지한다. 코드를 꼭 짧게 작성해야 한다는 것은 아니다. (예를 들어 `람다 함수`) 
- 다른 사람들이 사용하기 쉽고 테스트가 쉬워지기 때문에 읽기 쉽고 이해하기 쉬운 코드를 작성하기 위한 노력이다.
#KISS가 적용되지 않은 경우
f = lambda x: 1 if x <= 1 else x * f(x-1)

#KISS가 적용된 경우
def fac(number:int)->int:
	if number <= 1:
   	return 1
   else:
   	return number*fac(number-1)

3. 기술 부채

  • 생존이 더 중요하기 때문에 기술 부채가 점점 커짐. 코드의 품질보다는 속도에 초점을 맞추게 된다. 그로 인해 기술 부채가 발생한다.
  • 어느 시점에서는 코드/서비스 리팩토링을 통해 이 기술 부채를 갚아야 한다.
  • 트래픽이 몰리기 시작하면 사고가 발생할 수 있다. 테스트 작성을 노력하며 테스트 범위를 늘려야 하고 CI/CD를 도입해야 한다.

4. 코딩의 일반적인 원칙

📑 구글 파이썬 스타일 가이드

1) 일관된 포맷 및 이름 지정 규칙

  • 개인적으로 혹은 팀 내에서 이름 명명 규칙을 정하고 진행
  • 함수, 변수, 파일 이름은 이해하기 쉽고 약어를 피하는 것이 좋다.
  • 하지만 예외가 존재한다.
    • 보통 Single character name은 쓰지 않는 게 좋지만 counter를 쓰거나 iterators를 쓸 때는 글자 하나의 변수를 사용해도 된다.
    • try/except의 에러를 잡을 때 exception을 e라는 변수로 사용한다.
    • 파일을 다룰 때 f 변수를 사용해도 된다.
    • __로 시작하거나 끝나게 쓰면 안 된다. (파이썬의 경우예약 언어로 이해할 수 있음.)
    • 변수 이름만 봐도 데이터 타입이 분명하다면 변수명에 데이터 타입을 쓰지 않는다.

2) 의미 있고 설명적인 변수 이름 사용

3) 적절한 주석 및 문서화

  • 의미 없는 주석을 달 필요는 없다. i = i + 1 #i에 1 더하기 (이런 건 코드만 봐도 분명하게 알 수 있는 주석이기 때문에 1이 무슨 의미고 왜 1을 더하는지를 설명하는 게 의미 있는 주석이다.)

4) 제어 구조와 알고리즘 효율적인 사용

  • Generator 함수
  • List Comprehension
  • Lambda 함수
  • 알면 좋을 특정 언어의 기능들이 존재한다.

5. 좋은 코드 리뷰 방법

  • 코드를 요청하는 사람
    • 적어도 하루에 한 번 정도는 수정한 내용을 체크인한다. 그만큼 자주 체크인 하는 것이 좋다. 유닛 테스트와 같이 요청하면 좋다.
    • 주석을 최대한 추가하고 무슨 이유에서 뭘 하려고 하는 것인지 설명한다.
  • 코드를 리뷰하는 사람
    • 코딩 스타일에 대한 것보다는 코드 자체에 대해서 이야기하는 게 좋다.
    • 객관적으로 하고 비판적인 어조는 피한다.
  • 코드 리뷰에 편리한 툴 사용
    • 대표적인 게 github

6. 어떤 테스트들이 존재하는가?

1) Unit Test

  • 모듈의 특정 기능 (함수) 테스트. 보통 하나의 함수를 테스트해서 제대로 된 결과값이 나오는지 확인한다.
  • 특정 함수에 input을 주고 기대한 output이 나오는지 확인하는 것이며 가장 낮은 레벨의 기본 테스트.
  • Python에서 유닛 테스트를 하기 위해서는 import unittest를 사용한다.

2) Integration Test

  • 이런 다양한 함수(여러 모듈)를 하나의 차원에서 테스트한다.

3) Acceptance Test

  • 부하를 줬을 때도 (트래픽 등을 생성) 제대로 모듈들이 동작하는가.

4) UI Test

  • 요즈음은 Selenium 등의 툴을 이용해 웹 페이지 자체의 기능을 테스트하는 게 대세이다.

7. 테스트의 중요성

  • 테스트의 버그가 없다는 것을 확인하기 위해서.
  • 테스트가 많아질수록 시스템의 안정성이 증대되고 리팩토링 할 때도 더 이점이 있다.

8. Test Coverage

  • 실행 가능 경로 중 몇 퍼센트나 테스트가 되어 있는지 그 퍼센트를 나타낸다.
  • Test Coverage가 높을수록 시스템이 안정된다.

9. 파이썬에서 Unit Test 진행하는 방법

  • 보통 별도의 파일을 만들어서 테스트 스크립트를 작성한다.
  • 유닛 테스트를 위해 unittest 라이브러리를 import한다.
  • 테스트 클래스는 항상 unittest.TestCase를 상속받는다.
  • 이후 유닛 테스트할 함수에 input 값을 넣어 받은 output값과 예상된 output 값과 비교해 본다. (일치하면 테스트 통과)
  • cmd에서 -m unittest test_avg.py로 호출하여 테스트.
  • assertEqual(a, b)는 a, b가 일치하면 True 아니라면 False.
  • assertTrue(a) a가 True면 성공, False면 실패.
import unittest
import avg 

class averageTestCase(unittest.TestCase): # 테스트 클래스는 항상 testcase를 상속받는다
	def test_average(self):
    	answer = avg.compute_average([1, 2, 3, 4, 5])
        self.assertEqual(answer, 3.0) # input을 넣었을 때 나온 output 값인 answer가 3.0(원하는 결과값)인지 확인

	def test_empty_input_average(self):
    	answer = avg.compute_average([])
        self.assertEqual(answer, None)

if __name__ == "__main__":
	unittest.main()

✔ 파이썬으로 웹 데이터를 크롤하고 분석하기 (4)

1. Selenium - 브라우저 자동화하기

1) selenium 설치

  • Python을 이용해서 웹 브라우저를 조작할 수 있는 자동화 프레임워크.
  • 동적인 웹 페이지의 웹 크롤링을 위해 주로 사용한다.
  • selenium을 사용하기 위해서는 먼저 selenium을 설치해 주어야 한다.
%pip install selenium

2) webdriver 설치

  • WebDriver는 웹 브라우저를 제어할 수 있는 자동화 프레임워크.
  • 웹 브라우저와 연동을 하기 위해 필요하다.
%pip install webdriver-manager

3) selenium과 webdriver 호출

# selenium으로부터 webdriver 모듈을 불러옵니다.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

4) 웹 브라우저에 요청 보내기

  • webdriver.Chrome은 현재 우리가 사용하고 있는 Chrome과 동일한 버전을 사용해 싱크를 맞추기 위해 사용한다.
  • Chrome 드라이버 객체를 만들고 이를 driver라는 객체에 넣어 주는 과정이다.
  • Chrome 브라우저가 실행된다.
  • 또한 Requests와 동일하게 get 을 사용하는데 driver 객체에 get(url) 함수를 사용한다.
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("http://www.example.com") 

5) page_source를 통해 HTML 문서 확인

  • 응답을 받은 후 .page_source를 호출하면 응답을 받은 HTML 코드를 확인할 수 있다.
  • 이때 드라이버 객체를 만들게 되면 드라이버 객체가 소멸될 때까지 Chrome창이 켜진 채로 유지가 된다.
  • 어떤 명령을 함에 있어 Chrome창을 계속 켜고 있을 필요가 없다면 with-as 구문을 통해 주어진 명령이 끝나면 driver 종료가 되도록 처리해 준다.
#with 여기서 실행하는 것을 명령이 진행될 때까지는 as (변수명)에 해당하는 변수명으로 부르겠다는 선언
#with절 내부에서는 변수명으로만 호출해 주면 된다
#with절이 종료되면 driver도 자동 소멸된다
with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get("http://www.example.com")
    print(driver.page_source)

6) Driver의 특정 요소 추출

  • 요소를 하나만 찾으려면 .find_element(by, target), 요소가 두 개 이상이라면 .find_elements(by, target) 메소드를 사용한다.
  • 이때 by는 찾는 대상이 들어가면 된다. ex) By.ID, By.TAG_NAME, By.CLASS_NAME 등
  • target은 대상의 속성이 된다.
#.find_element를 사용하기 위해서는 By를 import 해 주어야 한다
from selenium.webdriver.common.by import By

with webdriver.Chrome(service=Service(ChromeDriverManager().install())) as driver:
    driver.get("http://www.example.com")
    #하나의 요소만 추출할 때 
    print(driver.find_element(By.TAG_NAME, "p").text)  
    #둘 이상의 요소를 추출할 때
    for element in driver.find_elements(By.TAG_NAME, "p"):
        print(element.text)

2.Wait and Call

1) Implicit Wait

  • 암묵적 기다림
  • 지정해 둔 특정 요소에 대한 제약을 통한 기다림이다.
  • .implicit_wait(초)로 호출을 해 주며 지정해 준 초 동안 기다리거나 지정한 초보다 더 빨리 렌더링이 된다면 완전히 렌더링이 될 때까지 기다려 준다.

2) Explicit Wait

3. 마우스 이벤트 처리하기

  • 마우스 이벤트의 종류
    • 마우스 움직이기 (move)
    • 마우스 누르기 (press down)
    • 마우스 떼기 (press up)
    • Selenium 공식 문서를 보면 다양한 Action을 확인 가능하다.
  • ActionChains을 통해 Action들을 연쇄적으로 수행할 수 있게 해 준다. 그 이후 Action들을 나열하고 마지막에 perform()으로 종료해 준다.
	button = driver.find_element(By.ID, "button")   #find_element를 통해 버튼 요소를 찾아 주고
	ActionChains(driver).click(button).perform()    #ActionChains을 통해 취할 모션을 나열해 준다.

4. 키보드 이벤트 처리하기

5. Jupyter Lab 사용하기

  • Enter: 명령 모드 -> 입력 모드
  • ESC: 입력 모드 -> 명령 모드
  • M : Code Cell (python 코드 실행 가능) -> Markdown Cell (Markdown 문법 활용)
  • Y : Markdown Cell -> Code Cell
  • A : 'Above' 상단의 새로운 Cell 추가
  • B : 'Below' 아래의 새로운 Cell 추가
  • dd : 현재 Cell 삭제
  • ctrl/cmd + Enter: 현재 Cell 실행 (code cell: 파이썬 코드 실행, markdown cell: 특정한 형식을 줄글로 표현)

🔎 어려웠던 내용 & 새로 알게 된 내용

1. .find_elements(by, target) 사용 시 주의할 것

  • .find_elements(by, target)로 추출되는 값은 두 개 이상의 요소로 리스트 형식이 된다.
  • 그렇기 때문에 driver.find_elements(By.TAG_NAME, "p").text 이렇게 바로 호출을 해 버린 후 print를 한다면 다음과 같은 오류가 발생한다.
  • AttributeError: 'list' object has no attribute 'text'
  • 만약 각 요소를 print 하고 싶다면 for문과 같은 반복문을 이용해서 print해 주어야 한다.

2. WebDriverWaitNameError

  • 명시적 기다림에 대한 처리를 해 주는 실습 과정에서 NameError: name 'WebDriverWait' is not defined 다음과 같은 오류가 발생했다.
  • 이 이유는 WebDriverWait 역시 selenium.webdriver.support.ui의 한 라이브러리도 따로 import를 해 줄 필요가 있었다.
  • 다음과 같은 NameError가 발생한다면 from selenium.webdriver.support.ui import WebDriverWait를 해 주었는지 확인해 보자. Explicit Wait를 위해서는 WebDriverWaitimport해 주어야 한다.

✍ 회고

- 사실 어떻게 코드를 짜야 좋은 코드가 될지에 대해서는 개발자로서 항상 고민하는 부분인 것 같다. 회사를 다닐 때도 다른 사람들의 코드를 보거나 내 코드를 보면서 정말 좋은 코드는 운영을 하면서 누가 기능을 보완하거나 수정하더라도 분석 시간이 오래 걸리지 않고 쉽게 이해할 수 있도록 풀어 있는 코드라는 생각이 들었다. 이번 특강을 통해서 그 부분을 더 확신하게 된 것 같다. 작년에 읽으려고 산 클린 코드라는 책을 아직 펼치지 못했다. 이번 기회를 삼아 보기 시작하면 어떨까라는 생각을 했다.

- Jupyter Lab과 관련해서 Markdown을 작성하는 법을 배웠는데 velog를 작성할 때와 동일해서 학습하는 데 어려움이 없었다. 그리고 간편 로그인과 같은 기능이 어떻게 구현이 되는지 궁금했는데 이 과정 역시 스크래핑을 통한 과정이라는 것을 알게 되었다. 다만 예시 사이트의 구조가 바뀌어서 임의로 네이버와 다음 사이트로 실습을 해 보았는데 보안 상의 문제 때문인지 바로 로그인이 되지는 않았다.

profile
송의 개발 LOG

0개의 댓글