이름이 명확
하고, 의미 있는 주석
, 일관된 형식
으로 체계적으로 구성되어야 한다.chatGPT
에게 물어보자.유닛 테스트
를 작성해야 한다.클래스
와 함수
는 분명하게 하나의 일을 하도록 구현해야 한다. (하나의 기능)모듈화
하거나 혹은 루프
를 돌려서 중복을 피해야 한다.로깅
을 잘해야 한다.운영 관점
에서 매우 중요하다.사용자
를 염두에 두고 설계한다. 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)
생존
이 더 중요하기 때문에 기술 부채
가 점점 커짐. 코드의 품질
보다는 속도
에 초점을 맞추게 된다. 그로 인해 기술 부채
가 발생한다.코드/서비스 리팩토링
을 통해 이 기술 부채를 갚아야 한다.테스트 범위
를 늘려야 하고 CI/CD를 도입
해야 한다.이름 명명 규칙
을 정하고 진행예외
가 존재한다. Single character name
은 쓰지 않는 게 좋지만 counter
를 쓰거나 iterators
를 쓸 때는 글자 하나의 변수를 사용해도 된다.try/except
의 에러를 잡을 때 exception을 e라는 변수
로 사용한다.파일
을 다룰 때 f
변수를 사용해도 된다. __
로 시작하거나 끝나게 쓰면 안 된다. (파이썬의 경우예약 언어
로 이해할 수 있음.)데이터 타입
을 쓰지 않는다.i = i + 1
#i에 1 더하기
(이런 건 코드만 봐도 분명하게 알 수 있는 주석이기 때문에 1이 무슨 의미고 왜 1을 더하는지를 설명하는 게 의미 있는 주석이다.)자주 체크인
하는 것이 좋다. 유닛 테스트
와 같이 요청하면 좋다.주석
을 최대한 추가하고 무슨 이유에서 뭘 하려고 하는 것인지 설명한다.코딩 스타일
에 대한 것보다는 코드 자체
에 대해서 이야기하는 게 좋다. github
모듈의 특정 기능 (함수)
테스트. 보통 하나의 함수를 테스트해서 제대로 된 결과값이 나오는지 확인한다.input
을 주고 기대한 output
이 나오는지 확인하는 것이며 가장 낮은 레벨
의 기본 테스트.import unittest
를 사용한다.다양한 함수(여러 모듈)를 하나의 차원에서
테스트한다. 부하를 줬을 때도 (트래픽 등을 생성)
제대로 모듈들이 동작하는가.웹 페이지 자체의 기능
을 테스트하는 게 대세이다.퍼센트
를 나타낸다.Test Coverage
가 높을수록 시스템이 안정된다.유닛 테스트
를 위해 unittest
라이브러리를 import
한다.테스트 클래스
는 항상 unittest.TestCase
를 상속받는다.유닛 테스트
할 함수에 input
값을 넣어 받은 output
값과 예상된 output
값과 비교해 본다. (일치하면 테스트 통과)-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()
Python
을 이용해서 웹 브라우저를 조작할 수 있는 자동화 프레임워크
.동적인 웹 페이지
의 웹 크롤링을 위해 주로 사용한다.selenium
을 사용하기 위해서는 먼저 selenium
을 설치해 주어야 한다.%pip install selenium
WebDriver
는 웹 브라우저를 제어할 수 있는 자동화 프레임워크
.웹 브라우저와 연동
을 하기 위해 필요하다.%pip install webdriver-manager
# selenium으로부터 webdriver 모듈을 불러옵니다.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
webdriver.Chrome
은 현재 우리가 사용하고 있는 Chrome
과 동일한 버전을 사용해 싱크
를 맞추기 위해 사용한다.driver
라는 객체에 넣어 주는 과정이다.Requests
와 동일하게 get
을 사용하는데 driver
객체에 get(url)
함수를 사용한다.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("http://www.example.com")
.page_source
를 호출하면 응답을 받은 HTML
코드를 확인할 수 있다.드라이버 객체
가 소멸될 때까지 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)
.find_element(by, target)
, 요소가 두 개 이상이라면 .find_elements(by, 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)
암묵적 기다림
지정해 둔 특정 요소에 대한 제약
을 통한 기다림이다..implicit_wait(초)
로 호출을 해 주며 지정해 준 초 동안 기다리거나 지정한 초보다 더 빨리 렌더링이 된다면 완전히 렌더링이 될 때까지 기다려 준다.명시적 기다림
지정한 시간
동안 기다린다.WebDriverWait()
를 사용해 주는데 이때 .until(조건)
을 줘서 조건이 만족할 때까지라고 지정할 수도 있고 .until_not(조건)
으로 해서 조건이 만족되지 않을 때까지로 지정해 줄 수도 있다.다양한 Action
을 확인 가능하다.ActionChains
을 통해 Action
들을 연쇄적으로 수행
할 수 있게 해 준다. 그 이후 Action
들을 나열하고 마지막에 perform()
으로 종료해 준다. button = driver.find_element(By.ID, "button") #find_element를 통해 버튼 요소를 찾아 주고
ActionChains(driver).click(button).perform() #ActionChains을 통해 취할 모션을 나열해 준다.
send_keys_to_element
를 통해 특정 키
를 입력한다. ActionChains
을 통해 Action
들을 연쇄적으로 수행
할 수 있게 해 준다. 그 이후 Action
들을 나열하고 마지막에 perform()
으로 종료해 준다.Enter
: 명령 모드 -> 입력 모드ESC
: 입력 모드 -> 명령 모드M
: Code Cell (python 코드 실행 가능) -> Markdown Cell (Markdown 문법 활용)Y
: Markdown Cell -> Code CellA
: '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
이렇게 바로 호출을 해 버린 후AttributeError: 'list' object has no attribute 'text'
- 만약 각 요소를
for문
과 같은 반복문을 이용해서
2.
WebDriverWait
의NameError
- 명시적 기다림에 대한 처리를 해 주는 실습 과정에서
NameError: name 'WebDriverWait' is not defined
다음과 같은 오류가 발생했다.- 이 이유는
WebDriverWait
역시selenium.webdriver.support.ui
의 한 라이브러리도 따로import
를 해 줄 필요가 있었다.- 다음과 같은
NameError
가 발생한다면from selenium.webdriver.support.ui import WebDriverWait
를 해 주었는지 확인해 보자.Explicit Wait
를 위해서는WebDriverWait
를import
해 주어야 한다.