MSSQL 테이블 변수, 임시 테이블 사용하기

취업 후 학습|2023. 12. 4. 17:49
728x90

실무에서 파트너사 알림톡 발송을 위해 대량의 데이터를 반복 Insert 할 일이 생겼는데 초기에 생각보다 시간이 너무 오래 걸려 효율적인 방법을 찾던 중 테이블 변수와 임시 테이블에 대해 알게 되어 정리할 겸 작성하기로 했다.

 

Join으로 조회한 데이터들을 While문을 통해 Row 접근으로 Update, Insert를 반복하는 작업인데 테이블 변수와 임시 테이블을 모르고 비효율적인 쿼리로 실행했을 땐 필요한 Data를 While문 안에서 계속 Join 하며 Select 했더니 아래 표처럼 기하급수적으로 시간이 늘어났다.

 

발송 Data Row 수 톡 발송 완료시간
50개 18초
100개 1분 10초
200개 4분 10초
300개 9분 20초
600개 37분 40초

 

시작한 직후엔 1초당 3개씩 발송하더니 시간이 갈수록 1초에 1개, 2초에 1개.... 이런 식으로 점점 딜레이가 됐었다.

 

운영계 DB에 불필요한 테이블을 만들지 않으려다 보니 이러한 결과가 나왔는데 테이블 변수나 임시 테이블을 사용하니 시간은 획기적으로 줄어들었다.

 

여담이지만 필요한 데이터를 파이썬으로 생성했더니 참 편한 것 같다.

 

테이블 변수


테이블 변수는 조회한 테이블 형태의 값들을 변수에 저장하는 것을 뜻한다.

 

변수이기 때문에 일반 변수와 동일하게 DECLARE로 "@"를 붙여 선언하고 메모리에 생성되기 때문에 쿼리가 실행되는 함수, 프로시저 안에서만 일시적으로 유효하다.

 

-- 테이블 변수 선언
DECLARE @TEMP_TABLE_NAME TABLE(
    -- 컬럼 선언
    COL1 VARCHAR(10),
    COL2 INT
)

-- 기존 테이블 전체 테이블 변수로 복사
INSERT INTO @TEMP_TABLE_NAME
SELECT * FROM ORIGIN_TABLE;

-- Join한 결과에서 특정 컬럼만 테이블 변수로 복사
INSERT INTO @TEMP_TABLE_NAME
SELECT A.COL1, A.COL2, B.COL1, B.COL2 FROM ORIGIN_TABLE_1 AS A
INNER JOIN ORIGIN_TABLE_2 AS B
ON A.COL1 = B.COL1;

-- 조회 예시
SELECT * FROM @TEMP_TABLE_NAME;

 

장점

  • 임시 테이블보다 빠르게 작동하며, 디스크 I/O를 줄일 수 있다.
  • 트랜잭션 로그에 기록되지 않으므로 롤백이 필요하지 않다.
  • 저장 프로시저, 함수, 트리거 등에서 사용할 수 있다.
  • 컴파일 시점에 결정되므로, 재컴파일을 유발하지 않는다.

단점

  • 통계를 생성하지 않아, 쿼리 최적화에 영향을 줄 수 있다.
  • 인덱스를 생성할 수 없으므로, 대량의 데이터를 처리하는 데는 부적합하다. (100건 이상의 데이터 사용 시 성능 저하)
  • 세션 간에 공유할 수 없다.
  • ALTER TABLE 명령을 사용하여 구조를 변경할 수 없다.

 

임시 테이블


임시 테이블은 일시적으로 데이터를 저장하고 조작하는 데 사용하는 테이블이며 시스템 DB인 tempdb에 저장된다.

 

테이블이기 때문에 CREATE을 사용하고 "#"을 붙여 임시 테이블을 선언한다.

 

이때 "#"지역 임시 테이블을 뜻하고 테이블 변수와는 다르게 "##"을 통한 전역 임시 테이블 선언이 가능하다.

 

즉, 전역 선언을 통해 사용자 본인의 다른 Session에서도 접근이 가능하다.

 

명시적으로 삭제하지 않더라도 DB와 연결된 Session이 종료되면 자동으로 삭제되며 전역 임시 테이블의 경우 마지막 Session이 종료되면 삭제되기 때문에 실무에서 테이블 조작하기 편하게 해주는 것 같다.

 

주로 복잡한 쿼리를 단순화하거나 중간 결과를 저장하고 여러 프로시저에서 결과를 공유하는 용도로 사용한다.

 

-- 지역 임시 테이블 선언 ('#' 한 개)
CREATE TABLE #TEMP_TABLE_NAME (
    -- 컬럼 선언
    COL1 VARCHAR(10),
    COL2 INT
)

-- 전역 임시 테이블 선언 ('#' 두 개)
CREATE TABLE ##TEMP_TABLE_NAME (
    -- 컬럼 선언
    COL1 VARCHAR(10),
    COL2 INT
)
-- 기존 테이블 전체 테이블 변수로 복사
INSERT INTO #TEMP_TABLE_NAME
SELECT * FROM ORIGIN_TABLE;

-- Join한 결과에서 특정 컬럼만 테이블 변수로 복사
INSERT INTO #TEMP_TABLE_NAME
SELECT A.COL1, A.COL2, B.COL1, B.COL2 FROM ORIGIN_TABLE_1 AS A
INNER JOIN ORIGIN_TABLE_2 AS B
ON A.COL1 = B.COL1;

-- 조회 예시
SELECT * FROM #TEMP_TABLE_NAME;

 

장점

  • 복잡한 쿼리를 단순화하고, 중간 결과를 저장하여 데이터 처리를 더 효율적으로 만들 수 있다.
  • 큰 데이터 세트를 작은 부분으로 분할하여 처리하면 성능이 향상될 수 있다.
  • 쿼리의 중간 결과를 확인하고 문제를 디버깅하는 데 도움이 된다.
  • 사용자 세션별로 생성되므로 다른 사용자가 접근할 수 없어 보안에 좋다.

단점

  • 트랜잭션 로그에 기록되므로, 많은 양의 데이터를 처리하면 로그 크기가 커질 수 있다.
  • 디스크 I/O 작업에 대한 Overhead가 존재한다.
  • 저장 프로시저 내부에서 실행할 때마다 재 컴파일이 발생한다.

 

적은 데이터(100건 이하)는 테이블 변수가 더 빠를 수 있지만 많은 데이터(100건 이상)는 임시 테이블이 성능적으로 유리하다.

 

필자는 애초에 비효율적인 쿼리였기 때문에 저장 프로시저 수정, 임시 테이블 적용, Update 필요 없는 행 로직 수행 제외 등 여러 차례에 걸쳐 튜닝한 결과 상당한 시간 단축을 이루었다.

 

다음은 데이터 1000건 기준의 테이블 변수와 임시 테이블 적용 후 알림톡 발송 실행 시간이다.

 

테이블 변수 실행 시간
테이블 변수 실행 시간 (24초)
임시 테이블 실행 시간
임시 테이블 실행 시간 (18초)

 

단일 Insert로는 최대 1000행이 가능하기 때문에 1000행을 기준으로 잡았으며 임시 테이블이 테이블 변수보다 25%가량 시간 단축 된 것을 확인할 수 있었다.

 

여태까진 쿼리에 대한 큰 고민을 하지 않은 것 같아 이번 경험을 계기로 끊임없이 고민하고 개선시키려 노력해야겠다.

728x90

댓글()

스프링 Annotation(어노테이션) 정리

취업 후 학습|2023. 6. 28. 16:01
728x90

업무를 하면서 다양하고 많은 Annotation에 대한 이해가 부족해 정리하고자 작성하기로 했다.

 

Lombok Annotation


@Setter

Class의 모든 필드에 해당하는 Setter 메서드 생성

 

@Getter

Class의 모든 필드에 해당하는 Getter 메서드 생성

 

@ToString

Class의 모든 필드에 해당하는 ToString 메서드 생성

 

@NoArgsConstructor

Class의 기본 생성자 추가

 

@RequiredArgsConstructor

Final이나 @NonNull인 필드만 파라미터로 받는 생성자 추가

 

@AllArgsConstructor

Class의 모든 필드 값을 파라미터로 받는 생성자 추가

 

@EqualsAndHashCode

Equals와 HashCode 메서드를 오버라이딩 하는 어노테이션

 

@Data

@Setter, @Getter, @ToString, @NoArgsConstructor 등 Lombok에서 제공하는 필드와 관련된 모든 코드 추가 (모든 기능 사용 가능하므로 Risk 존재해서 사용하지 않는 게 좋음)

 

JPA Annotation


@Entity

실제 DB 테이블과 매핑될 Class

 

@Table(name = "")

실제 DB의 name에 해당하는 테이블과 매핑(생략할 경우 Class명과 테이블명 매핑)

 

@Id

해당 테이블의 PK 필드

 

@Column(name = "")

name에 해당하는 컬럼과 매핑(생략할 경우 필드명과 컬럼명 매핑)

 

Spring Annotation 


@Controller

해당 Class는 Controller란 것을 명시하고 Spring에서 Bean으로 등록 (Bean이란 Spring에서 관리하는 자바 객체)

 

@Service

해당 Class는 비즈니스 로직을 수행하는 Service란 것을 명시하고 Spring에서 Bean으로 등록

 

@Repository

해당 Class는 DAO인 것을 명시하고 Spring에서 Bean으로 등록 (Spring에서 지원하지 않는 Exception을 Spring Exception으로 전환)

 

@Component

해당 Class는 Component란 것을 명시하고 Spring에서 Bean으로 등록 (@Controller, @Service, @Repository의 상위 어노테이션이므로 3가지 전부 @Component으로 대체 가능)

 

@Bean

개발자가 제어 불가능한 외부 라이브러리 등을 Bean으로 등록

 

@ComponentScan

@Component, @Controller, @Service, @Repository 가 부여된 Class 들을 검색해서 Bean으로 등록

 

@RequestMapping(value = "")

URI의 요청이 value와 일치할 경우 해당 클래스나 메서드 실행

 

@RequestParam(value = "", required = false)

메서드 파라미터 앞에 쓰며 Http 요청의 파라미터 값(value)을 메서드 파라미터로 받음

required 옵션의 Default는 true이며 true일 경우 해당 파라미터가 없을 때 400 에러 발생

 

@RequestBody

메서드 파라미터 앞에 쓰며 Http 요청의 Body를 자바 객체로 변환해서 매핑된 메서드 파라미터로 전달

 

@RequestHeader(value = "")

메서드 파라미터 앞에 쓰며 Request의 Header값을 메서드의 파라미터로 받는 어노테이션

 

@ResponseBody

자바 객체를 Http 요청의 Body로 변환(content-type을 application/json으로 설정 필요)

 

@RestController

@Controller와 @ResponseBody를 합친 어노테이션

 

@ModelAttribute

View단에서 전달하는 파라미터를 메서드의 파라미터로 변환해 주는 어노테이션(태그의 name과 클래스의 멤버 변수명, Set 메서드명 일치)

 

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping

각 Get, Post, Put, Delete, Patch 요청에 대한 Annotation

 

@Valid

파라미터 객체 앞에 쓰며 유효한 객체인지 검사

 

@CookieValue(value = "", required = false)

쿠키의 value 값을 받아오며 required의 Default는 true

required = false를 사용하지 않은 상태에서 해당 쿠키값이 존재하지 않다면 500 에러를 발생시키고 사용하면 존재하지 않을 때 null로 받음

 

@Lazy

Class가 실제 사용될 때 Spring에서 Bean등록을 하게 되는 지연 로딩

 

@Autowired

Field, Setter, Constructor 3가지에 사용하며 Type에 따라 Bean을 주입

 

@Qualifier("id")

@Autowired와 같이 쓰며 같은 타입의 Bean 객체가 있을 경우 명시된 ID로 원하는 Bean을 주입하는 어노테이션

 

@PathVariable

메서드 파라미터 앞에 쓰며 URI에서 특정 값을 메서드 파라미터로 받아오는 어노테이션

특정 값은 URI에서 {}로 감싸고 파라미터 명과 같음

 

@Transactional

메서드 내부의 DB 트랜잭션 설정하는 어노테이션

 

@Scheduled(cron = "")

Cron 표현식에 의한 정해진 시간에 메서드를 실행시키는 어노테이션

 

@Test

Junit에서 테스트할 대상으로 지정

728x90

댓글()

스프링이란? 스프링과 스프링 부트의 차이점은?

취업 후 학습|2023. 6. 20. 14:26
728x90

이미 Spring Boot를 이용한 웹 업무를 하고 있지만 유지보수 수준이기 때문에 구체적인 이해가 부족한 듯싶어 처음부터 정리할 겸 작성하기로 했다.

 

Spring 이란?


Spring은 Java 개발을 편하게 해주는 프레임워크이며 웹 MVC 모듈을 제공하여 동적인 웹을 효과적으로 제작할 수 있다.

 

또한 ORM(JPA, Mybatis)과 같은 모듈들을 보다 편하게 사용할 수 있다는 장점이 있다.

 

이때 ORM은 Object Relational Mapping의 약자로 객체와 관계형 데이터 베이스의 데이터를 자동으로 Mapping 해주는 것을 뜻한다.

 

추가적으로 https://start.spring.io/ 해당 사이트에서 초기 프로젝트를 생성하는 서비스를 제공하기도 한다.

 

Spring은 자바 객체의 생명 주기를 관리하기 때문에 사용자는 컨테이너에서 필요한 객체를 가져와 사용만 하면 된다는 편리함이 있으며 대표적인 특징으로는 DI, IOC가 있다. 

 

DI는 Dependency Injection(의존성 주입)의 약자로 어떤 객체를 사용하는 주체가 객체를 직접 생성하는 것이 아니라 Spring에서 생성한 객체를 주체에게 주입시켜 주는 방식을 뜻한다.

 

객체를 직접 생성하게 되면 수정 사항이 생길 때마다 수정할 부분이 많아지는데(의존성이 높아지는데) Spring에서 생성하게 되면 Interface를 기반으로 관리하기 때문에 수정할 부분이 줄어든다(의존성이 낮아진다).

 

이때 Interface를 사용하는 이유는 Controller 단에서 직접 Service를 바라보는 게 아닌 Interface를 바라보고 있는데 Interface에 대한 실제 구현체가 Service단에 있으므로 Runtime 직전까지는 실제 구현체를 알지 못한다.

 

따라서  Controller 단에서는 실제 비즈니스 로직에 대해 알 필요가 없고 Interface의 메서드로만 로직을 구성하면 된다.

 

반대로 Service 단에서는 Interface의 규칙만 잘 지킨다면 언제든 로직 변경이 가능하고 새로운 클래스로 대체도 가능하기 때문에 의존성이 낮아져 확장성이 높다는 장점이 있다.

 

IOC는 Inversion Of Control(제어의 역전)의 약자로 간단하게 말하자면 사용자의 제어권을 Spring에게 넘기는 것을 뜻한다.

 

일반적인 Java Programming의 흐름은 사용자가 직접 객체를 생성하고 메서드를 호출하는 방식이었지만 Spring에서는 Spring이 객체를 생성하고 주입하여 객체의 메서드를 호출하는 방식이다.

 

따라서 Spring이 실행될 때 모든 의존성 객체를 생성하고 필요한 곳에 주입해 준다.

 

Spring과 Spring Boot의 차이점


결론부터 말하자면 Spring Boot는 Spring의 복잡성을 줄인 업그레이드 버전이라고 생각하면 된다.

 

Spring은 Dependency 설정을 위해 https://mvnrepository.com/ 같은 곳에서 일일이 찾아야 하고 버전 관리도 직접 해야 해서 번거롭고 복잡하지만 Spring Boot는 spring-boot-starter를 통해 자동으로 설정할 수 있어 훨씬 더 짧은 시간에 비교적 간단하게 실행할 수 있다.

 

또한 Spring은 주로 maven 기반의 Java Application을 만드는 데 사용하지만 Spring Boot는 Gradle 기반의 REST API 개발에 사용하며 톰캣 같은 WAS가 내장되어 있어 쉽게 Web Application 구동이 가능하며 실행 가능한 JAR 파일 생성도 가능하여 자바 옵션만으로 쉽게 배포가 가능하다.

 

정리하자면 Spring은 프레임워크를 보다 세밀하게 제어하는 경우, Spring Boot는 빠르게 간단하게 Application 개발을 하는 경우에 적합하다.

 

 

728x90

댓글()

리액트 data.map is not a function 에러 해결

취업 후 학습|2023. 5. 3. 10:49
728x90

리액트에서는 return 문에서 JSX를 사용하기 때문에 직접적으로 for문과 같은 반복문을 사용하지 못한다.

 

굳이 사용하자면 JSX를 반환하는 함수를 선언하고 그 안에 반복문을 사용할 순 있겠지만 코드가 짧아질수록 편의성이 증가하므로 주로 map을 쓰는 편이다.

 

map 함수 에러
map 함수 에러

하지만 종종 위와 같이 map is not a function 에러가 나타나는 경우가 있다.

 

문제는 두 가지 경우로 나눌 수 있는데 첫 번째는 data 부분이 배열이 아닌 경우고, 두 번째는 data 변수가 배열로 데이터를 받기 전 렌더링이 되었을 경우다.

 

map 함수는 배열에 접근하기 위한 자바스크립트의 내장 함수이기 때문에 배열이 아닌 데이터(특히 객체)에 map을 사용할 경우 문제가 발생한다.

 

따라서 콘솔에 해당 변수를 찍어보면 배열이 아닌 경우가 대다수일 것이다.

 

하지만 간혹 가다 분명히 배열이 맞는데 위와 같은 에러가 나오는 경우가 있다.

 

이것은 두 번째 케이스인 변수가 배열의 데이터를 받기 전에 렌더링 되었을 경우다.

 

물론 useState([])처럼 빈 배열로 초기화하면 되지만 상황에 따라 불가능한 경우도 있을 것이다.

 

그럴 땐 데이터를 받아왔을 때만 컴포넌트를 렌더링 하면 된다.

 

즉, 문제 되는 변수가 data 라면 {data && data.map()}처럼 data 변수에 값을 받아왔을 때만 map 함수를 실행시키도록 변경하면 문제는 해결된다.

 

이때 중괄호({})로 감싸주는 것을 잊으면 안 된다.

 

 

=====  리액트를 처음으로 접해서 공부할 겸 정리한 것이라 적극적인 피드백 환영합니다!! =====

728x90

댓글()

파이썬 Pyautogui로 매크로 만들기

취업 후 학습|2023. 2. 26. 18:54
728x90

파이썬은 인터프리터 언어라 컴파일 언어에 비해 비록 느리지만 사용자 친화적으로 쉬운 구문과 광범위한 응용 프로그램에 사용할 수 있단 장점이 있다.

 

특히 강력한 라이브러리로 데이터 분석, AI, 자동화 등에 강점을 보이는데 이번에는 자동화(매크로)에 자주 쓰이는 Pyautogui에 대해 알아보자.

 

시작 전에 처음 들어봤거나 확실하게 알지 못하는 사람들을 위해 매크로가 무엇인지 알아보자.

 

매크로란 컴퓨터 과학 분야에서 정해진 순서에 따라 특정한 입력 시퀀스가 출력 시퀀스를 정의하는 규칙이나 패턴을 말한다. (출처 : 위키백과)

 

즉, 쉽게 풀어서 말하자면 정해진 반복 작업을 컴퓨터가 수행하는 것이다.

 

특히 게임이나 티켓팅, 수강신청 등에서 매크로란 단어를 많이 접했을 텐데 이들의 공통점이 정해진 위치를 반복적으로 이동, 클릭, 입력한다는 점이다.

 

로직만 잘 구성한다면 불필요한 반복 작업을 컴퓨터에게 대신시킬 수 있기 때문에 아주 유용한 스킬이다.

 

Pyautogui 사용하기


그럼 Pyautogui를 설치해 보자.

 

필자는 윈도우 환경이며 Visual Studio Code를 사용하기 때문에 터미널에 바로 입력하면 된다.

 

Pyautogui 설치 로그
Pyautogui 설치 로그

 

명령어는 pip install pyautogui이고 위 사진처럼 Successfully installed 로그가 찍힌다면 설치가 완료된 것이다.

 

이제 Pyautogui를 설치했으니 관련 함수들을 알아보자.

 

함수들은 크게 마우스, 키보드, 이미지 관련 함수로 나눌 수 있는데 상당히 함수가 많기 때문에 그중에서도 자주 쓰일만한 함수들만 정리했다. 

 

특히 빨간 표시는 생략 가능한 옵션을 뜻한다.

 

 마우스 관련 함수
 position()  마우스의 현재 위치 좌표 출력
 mouseInfo()  마우스의 죄표, RGB Color 등 관련 정보
 moveTo(x, y, n)  좌표 (x, y)로 n 초 동안 이동
 dragTo(x, y, n)  좌표 (x, y)로 n 초 동안 드래그
 click(button=’right’)  왼쪽 클릭 (오른쪽 클릭)
 click(clicks=n, interval=t)  왼쪽 클릭 (t초 간격으로 n번 클릭)
 doubleClick()  왼쪽 더블 클릭
 scroll(x)  x만큼 스크롤 이동(양수면 위, 음수면 아래)
 키보드 관련 함수
 write(‘String’, interval=n)  영어 문자열을 n초 마다 입력 (한글 지원 X)
 keyDown(‘key’)  key에 해당하는 키를 누름
 keyUp(‘key’)  key에 해당하는 키를 뗌
 press([‘key’], presses=n, interval=t)  리스트에 있는 키를 t초에 한 번씩 n 입력
 hotkey(‘key1’, ’ key2’)  key1, key2 입력
 KEYBOARD_KEYS  입력 가능한 키 리스트
 이미지 관련 함수
 screenshot(‘test.png’, region=(x1, y1, x2, y2))  스크린샷 이미지 객체 반환, test.png 명으로 파일 저장, (x1, y1) 부터  (x2, y2)까지의 영역
 locateOnScreen(‘test.png’)  test.png 와 일치하는 영역 반환(left, top, width, height)
 locateAllOnScreen(‘test.png’)  test.png 와 일치하는 모든 영역 반환(left, top, width, height)
 locateCenterOnScreen(‘test.png’)  test.png 와 일치하는 영역의 중앙 좌표 반환
 기타 함수
 size()  주 모니터 크기 반환(width, height)
 countdown(n)  n초 동안 카운트 다운하며 각 숫자를 출력
 PAUSE = t  각 함수별 t초 딜레이(Default = 0.1초)

 

특히 매크로는 정해진 좌표를 반복 클릭하기 때문에 구현 단계에서 좌표를 얻을 수 있는 mouseInfo()가 상당히 유용한 함수다.

 

주의해야 할 점은 키보드 관련 함수에서 알파벳키를 입력하고 실제 입력 상황에서 한글로 되어 있다면 상관없지만 write로 직접 한글 입력은 지원하지 않기 때문에 한글 입력을 위해선 편법을 위한 추가 사항이 조금 필요하다.

 

바로 pyperclip을 설치해서 클립보드에 한글을 복사 후 붙여 넣기 하는 방식이다.

 

명령어는 pip install pyperclip이며 pyautogui를 설치할 때처럼 동일하게 하면 된다.

 

import pyperclip, pyautogui

# 클립보드에 한글 문자열 복사
pyperclip.copy('한글 입력 테스트')

# ctrl + v 로 붙여넣기
pyautogui.hotkey('ctrl', 'v')

 

설치 후 위 코드처럼 작성하면 한글을 입력하는 듯한 효과를 볼 수 있다.

 

모든 함수를 사용한 예제를 보기엔 너무 방대하므로 간단하게 특정 위치를 반복 클릭하는 매크로와 임시 비밀번호를 생성해서 저장하는 예제를 살펴보자.

 

반복 클릭 매크로


import pyautogui

# 첫 시작 위치로 이동
pyautogui.moveTo(334, 351)

# 시작 위치에서 마우스가 벗어나면 반복 중지
while pyautogui.position().x == 334 and pyautogui.position().y == 351:
    pyautogui.click()
    pyautogui.moveTo(320, 446)
    pyautogui.click()
    pyautogui.moveTo(334, 351)

 

위 코드는 특정 위치를 무한 반복 클릭하는 코드다.

 

이때 주의해야 할 점은 마우스를 다루기 때문에 딜레이나 무한 루프를 탈출할 조건을 사용하지 않을 경우 이도저도 못하는 난감한 상황이 발생할 수 있다.

 

해당 예제에서는 시작 위치를 정하고 마우스가 위치를 벗어날 경우 프로그램을 종료하도록 작성했다.

 

위와 같은 로직으로 미리 mouseInfo() 함수를 이용해서 좌표 정보를 얻고 작성하면 빠르게 반복 클릭할 수 있다.

 

임시 비밀번호 생성 후 저장


import pyautogui, random, time

# 길이가 8인 소문자 임시 비밀번호 생성
def temp_pw():
    pw = ''
    for i in range(8):
        pw += chr(random.randrange(97,123))
    return pw

# 메모장 위치
pyautogui.moveTo(496, 423)
pyautogui.doubleClick()

# 메모장이 켜질때까지 대기
time.sleep(1)

# 메모장에 커서 생성
pyautogui.moveTo(604, 713)
pyautogui.click()

# 5개의 임시 비밀번호 생성
for i in range(5):
    pyautogui.write(temp_pw()+'\n')

# 저장
pyautogui.hotkey('ctrl', 's')

# 메모장 종료
pyautogui.hotkey('alt', 'f4')

 

필자는 임시 비밀번호를 저장하기 위한 메모장을 생성했었고 해당 위치 좌표로 이동 후 더블 클릭으로 켰다.

 

너무 속도가 빨라 엉뚱한 곳을 클릭하는 것을 방지하기 위해 메모장이 켜질 때까지 대기하려 time.sleep(1)을 했다.

 

메모장이 켜지면 커서를 생성하기 위해 한번 클릭을 했고 소문자로 이루어진 8자리 임시 비밀번호를 저장 후 종료하는 로직이다.

 

이처럼 파이썬의 자동화 도구를 잘 사용하면 불필요한 반복 작업을 컴퓨터에게 시킬 수 있어 상당히 편리하고 개인에게 강력한 무기가 될 수 있다고 생각한다.

 

필자도 종종 필요한 경우 자동화 코드를 작성해서 편리했고 이를 본 주위 사람들이 신기해했던 경험이 있기 때문이다.

 

차후에 단순한 마우스, 키보드 자동화 말고도 엑셀 파일처럼 업무 자동화에 대해서도 가능한 자세히 포스팅하도록 하겠다.

728x90

'취업 후 학습' 카테고리의 다른 글

스프링이란? 스프링과 스프링 부트의 차이점은?  (0) 2023.06.20
리액트 data.map is not a function 에러 해결  (0) 2023.05.03
jQuery로 요소 찾기  (0) 2023.01.03
SAP ERP란  (0) 2022.09.29
Git Branch 전략  (0) 2022.08.17

댓글()

안드로이드 스튜디오 딜레이(Delay) 구현

728x90

앱을 구현하다 보면 병렬 처리가 필요할 경우 스레드를 사용하면 되지만 상황에 따라서 일정시간 뒤에 전체 로직을 수행해야 할 경우가 있다.

 

필자 같은 경우 현재 구현하고 있는 카드 뒤집기 게임에서 일정시간 전체 카드를 보여주고 다시 뒤집는 로직을 수행하거나 두 번째 오픈한 카드가 일치하지 않을 때 일정시간 이후 다시 뒤집어야 되는데 여기서 딜레이가 필요했기 때문에 정리하기로 마음먹었다.

 

결론부터 말하자면 Handler의 postDelayed 메서드를 사용하면 된다.

new Handler().postDelayed(new Runnable(){
        @Override
        public void run(){
            //구현하고 싶은 코드
        }
    }, 5000);	//5초

 

Handler의 postDelayed 메서드에 Runnable 인터페이스를 파라미터로 주고 run을 실행시키는 형태이다.

 

이때 두 번째 파라미터는 딜레이하고 싶은 시간인데 기준이 ms(미리 세컨드)이므로 1초당 1000ms로 계산해서 넣으면 된다.

 

new Handler().postDelayed(new Runnable(){
        @Override
        public void run(){
            for (int i = 0; i < idIndex; i++){
                animator = ObjectAnimator.ofFloat(findViewById(i),"rotationY",0,180);
                animator.start();
            }
            clickControl(true);
            if(player == 1){
                stt = new SoloTimerThread();
                t = new Thread(stt);
            }else{
                dtt = new DuoTimerThread();
                t = new Thread(dtt);
            }
            t.start();
        }
    }, 5000);

위 코드는 5초의 딜레이를 주고 모든 카드들을 y축을 기준으로 180도 뒤집은 다음, 모드에 따라 해당하는 스레드를 실행시키는 예제이다.

 

의외로 스레드만큼 딜레이는 유용하므로 코드를 참고하여 적절히 사용하면 좋을 것 같다.

728x90

댓글()

안드로이드 스튜디오 스레드(Thread) 구현하기

728x90

카드 맞추기 앱을 구현하면서 타이머 때문에 스레드를 다루게 되어 정리할 겸 포스팅하기로 했다.

 

하나의 동작하는 프로그램을 프로세스(Process)라고 하며 하나의 프로세스는 한 가지 일밖에 하지 못한다.

 

하지만 프로세스 안에 있는 스레드를 이용한다면 두 가지 이상의 일을 동시에 할 수 있다.

 

물론 보다 정확히 말하자면 동시에 하는 것처럼 보이게 할 수 있는 것이다.(스레드에 대해 자세한 정리는 기회가 되면 포스팅하겠다.)

 

이를 병렬 처리라 하며 안드로이드의 자바 코드로 스레드를 구현하는 방법은 두 가지가 있다.

 

첫 번째는 Thread 클래스를 상속받는 방법이고 두 번째는 Runnable 인터페이스를 구현하여 Thread 객체에 전달하는 방법이다.

 

Thread 클래스


public class MyThread extends Thread {
    // run() 메소드를 오버라이드
    public void run() {
    	// 수행할 로직
        System.out.println("Hello from a thread!");
    }

    public static void main(String[] args) {
        // MyThread 객체를 생성한 후, start() 메소드를 호출하여 실행
        MyThread thread = new MyThread();
        thread.start();
    }
}

위 내용은 스레드를 상속받아 사용하는 코드이다.

 

start() 메서드를 실행하면 Thread 클래스 내부에서 run() 메서드를 실행하기 때문에 꼭 thread.run()이 아닌 thread.start()로 하는 것을 잊으면 안 된다.

 

Runnable 인터페이스


Thread는 Class를 상속(Extends) 받기 때문에 Java의 단일상속에 의해 다른 Class를 상속받을 수 없어 유용하지 않은 편이다.

 

또한 Thread를 상속받을 경우 Thread Class에 구현된 모든 코드들이 동작하기 때문에 자원을 많이 잡아먹어 효율적이지 못하다.

 

이를 해결하기 위해 Runnable 인터페이스를 구현(Implements)하고 Thread 객체에 전달하면 대부분의 구현을 할 수가 있다.

 

public class MyRunnable implements Runnable {
    // run() 메소드를 오버라이드
    public void run() {
    	// 수행할 로직
        System.out.println("Hello from a thread!");
    }

    public static void main(String[] args) {
        // MyRunnable 객체를 생성한 후, 이를 Thread 생성자에 전달
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

 

위 내용은 Runnable 객체를 생성한 후 Thread 생성자에 전달해서 Thread를 생성하고 실행한다.

 

이때 생성한 Runnable 객체는 다른 클래스에서도 사용할 수 있기 때문에 유연한 코드 작성이 가능하다.

 

Thread는 상속이고 Runnable은 Interface이기 때문에 다중 상속이 불가능한 Java에서 Runnable 객체는 아주 유용한 작성방법이다. 

 

Runnable 인터페이스를 이용한 Thread 수행 시에도 내부적으로 Run() 메서드를 오버라이드 하기 때문에 thread.run()이 아닌 thread.start()를 해야 한다.

 

Thread를 사용하는 대표적인 예시가 타이머이기 때문에 마지막으로 Runnable Interface를 이용한 타이머를 예제로 설명하겠다.

 

public class MyRunnable implements Runnable {
    // run() 메소드를 오버라이드
    public void run() {
    	// 스레드가 실행할 로직 구현
        int second = 0;
        System.out.println("===== 타이머 실행 =====");
        // 타이머에 자주 쓰는 코드
        while (true){
            System.out.println("실행시간 : " + second);
            try {
                Thread.sleep(1000); 	// 1초 딜레이
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
            second++;
        }
        System.out.println("===== 타이머 종료 =====");
    }

    public static void main(String[] args) {
        // MyRunnable 객체를 생성한 후, 이를 Thread 생성자에 전달
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

 

위 예제는 While문을 통해 무한 루프를 돌면서 Thread.sleep으로 1초간 딜레이를 주는 타이머다.

 

이때 종료하고 싶은 시점에서 thread.stop()이나 thread.interrupt()를 호출하면 Run() 메서드 내부에서 Catch문에 걸려 종료할 수 있다.

 

thread.stop()은 불완전한 메서드이기 때문에 thread.interrupt()를 추천한다.

728x90

댓글()

jQuery로 요소 찾기

취업 후 학습|2023. 1. 3. 11:11
728x90

먼저 jQuery란 HTML 요소들을 쉽게 조작하기 위해 JavaScript로 작성된 라이브러리이다.

 

jQuery로 작성하면 코드의 길이도 줄어들고 직관적이라 가독성도 좋기 때문에 많이 사용하는 듯싶다.

 

jQuery는 외부 라이브러리이기 때문에 사용하려면 js파일을 다운로드하거나 CDN 코드를 삽입하면 되는데 CDN 코드가 더 편하므로 이 방법을 소개하겠다.

 

https://releases.jquery.com/

 

jQuery CDN

The integrity and crossorigin attributes are used for Subresource Integrity (SRI) checking. This allows browsers to ensure that resources hosted on third-party servers have not been tampered with. Use of SRI is recommended as a best-practice, whenever libr

releases.jquery.com

 

해당 페이지로 이동하면 jQuery 버전에 맞춰서 Script코드를 받을 수 있는데 uncompressed는 압축하지 않은 원문 Script, minified는 압축한 Script를 뜻한다.

 

압축이 된 Script는 비교적 코드를 이해하기 어렵기 때문에 실사용자에게 제공되는 운영계에서 주로 사용하기 때문에 개발계에서 사용하기 용이한 uncompressed를 누르면 된다.

 

uncompressed
uncompressed

 

혹시 몰라서 복사한 Script를 첨부하도록 하겠다.

 

<script src="https://code.jquery.com/jquery-3.6.3.js" 
    integrity="sha256-nQLuAZGRRcILA+6dMBOvcRh5Pe310sBpanc6+QBmyVM=" 
    crossorigin="anonymous">
</script>

 

해당 코드를 jQuery를 사용하고자 하는 파일 상단 (주로 head)에 추가하면 이제 자유롭게 jQuery를 사용할 수 있다.

 

document.getElementByID("ID 명");
document.getElementByClassName("Class 명");

JavaScript는 위 코드처럼 ID와 Class를 통해 요소를 찾아야 한다.

 

또한 Name으로는 찾을 수 없어 querySelector로 태그와 자식 요소로 찾아 들어가야 하는 불편함이 있다.

 

$("#ID 명")
$(".Class 명")
$("[name=name 명]")

하지만 위와 같이 jQuery를 사용한다면 짧은 문장으로 쉽게 요소를 찾을 수 있다는 장점이 있다.

 

jQuery 옵션으로 텍스트를 변경하는 text()나 속성을 다루는 attr() 등의 편리함이 있는데 이와 관련해서는 기회가 된다면 포스팅하도록 하겠다.

728x90

댓글()