누군가 나에게 "기회일지 손해일지 모르는 상황에서 너는 어떤 것을 선택하는 편이냐" 라고 묻는다면, 대개 전자를 택하는 편이다.

Me : If someone asks me like, "what would you choose between a chance or disadventage but you dont know what it is" then I mostly choose the former one.

ChatGPT : If someone were to ask me, "In a situation where you don't know if it will be an opportunity or a loss, which would you choose?" I would usually go for the former.

 

손해가 있을지언정, 기회가 될 수 있는 미래를 놓치고 싶지 않기 때문이다.

Me : Even if It will give me a disadventage, I dont want to miss the chance which could lead me a good future

ChatGPT : Even if it results in a loss, I don't want to miss out on potential opportunities in the future.

 

그 손해를 감수할 수 있도록, 그리고 기회를 많이 만들 수 있도록 평소에 준비를 많이 해둬야한다.

Me : I must be prepare in daily life to make chances more and overcome the disadventages.

ChatGPT : To take on such risks and create more opportunities, I need to be well-prepared in advance.

 

오늘은 많이 바쁘지 않았다.

Me : it wasnt busy today.

ChatGPT : Today was not very busy.

 

프로젝트도 잘 진행되고 있고, 저녁 모임도 잘 즐기고 올 수 있을 것 같다.

Me : The project is going well and I would enjoy a meeting this evening.

ChatGPT : The project is progressing well, and I think I can enjoy the dinner gathering tonight.

 

저녁 모임 이후에 코인노래방 가서 스트레스를 좀 풀어야겠어.

Me : Need to go to Coin Noraebang to release my stress.

ChatGPT : After the dinner, I should go to a Coin Noraebang to relieve some stress.

 

영어 공부를 구체적으로 어떻게 하면 좋을지에 대해 고민을 많이 했다.

Me: I've considered how to study Eng in detail.

ChatGPT : I've been thinking a lot about how to specifically improve my English.

 

영어는 내가 노력 한 만큼 실력이 상승 할 테지만, 외워야 할 것이 너무 많아.

Me: My Eng skill would be increased as I tried, but too many things to memorize.

ChatGPT : English skills will improve as much as I put in the effort, but there's so much to memorize.

 

하지만 내게 주어진 시간은 계산 가능한 범위 내에 있어.

Me : but the time I get is in the boundary which I can calculate.(??)

ChatGPT : However, the time I have is within a calculable range.

 

즉, 한정적이란 소리지.

Me : It means, my time has limit.

ChatGPT : In other words, it's limited.

 

그래서, 되도록 영어에 노출이 많이 되려고 한다.

Me : So, I will be exposed to Eng as much as possible.

ChatGPT : So, I'm trying to expose myself to English as much as possible.

 

영어 자체가 생활이 되면, 아기가 언어 배우듯이 자연스럽게 익혀지지 않을까 생각한다.

Me : I think i would learn Eng naturally as the babies learn if English has a big part of my life.

ChatGPT : I think if English becomes a part of my daily life, I'll pick it up naturally, like a baby learning a language.

 

영어 쉐도우 복싱 가능한 유튜브를 많이 보고, 많이 듣고, 또 직접 많이 말 해봐야겠다.

Me : should watch Eng shadowing video from youtube, listen a lot, and speak outside a lot.

ChatGPT : I need to watch a lot of Youtube videos for English shadowing, listen a lot, and speak a lot.

 

그 작업 중 하나가 이렇게 유튜브를 통해서 이야기 하는 것이다.

Me : the one of process is to speak out through youtube as this.

ChatGPT : One part of this effort is talking like this through Youtube.

 

얼른 유의미한 결과가 나왔으면 좋겠다.

Me : Hope to meet the meaningful result as much as possible.

ChatGPT :  I hope to see meaningful results soon.

 

https://youtu.be/OQpPSigLEI8?feature=shared

 

 

 

 

 

 

 

 

 

 

'English' 카테고리의 다른 글

Study English 24.06.02  (0) 2024.06.02
Study English 24.06.01  (1) 2024.06.01
Study English 24.05.30  (0) 2024.05.30
Study English 23.09.05  (0) 2023.09.19
Study English 23.09.04  (0) 2023.09.19

 

매일 하는 영어공부 다시 시작하려고 한다.

Me : I'm gonna start to study English daily since today.

Gemini : I've decided to start studying English daily again.

 

작년에는 9월 글을 마지막으로 멈춰버렸는데, 이유는 바빠서가 아니고 그냥 힘들어서.

Me : I stopped this way to study Eng in Sep last year not because I was busy but It was very hard to keep doing it.

Gemini : I stopped studying English in September last year, not because I was busy, but because I found it difficult to continue

매일 글을 쓴다는 것 그리고 영어로 번역하고 틀린지 맞는지 확인하는 작업까지 한다는 것은,

보통 의지가 아니고선 계속 지속하기가 아주 힘든 것인 듯 하다.

Me : Literally it was very hard to me without strong will to keep writing something every day,

translating, and checking my English is correct or not.

Gemini : I realized that the process of writing daily, translating into English, and checking for spelling and grammar mistakes is something that is really difficult to do consistently without strong willpower. It can be quite challenging.

 

그럼에도 불구하고 다시 시작하는 이유는,

이렇게 힘든 과정을 거쳐야 억지로라도 영어 공부를 할 수 있을 것 같고

기억에도 더 오래 남을 것 같아서이다.

Me : The reasons why I start it again are to push myself to study English through this hard process,

and to memorize for a longer time.

Gemini : Despite this, I decided to start again because I think that going through this difficult process will allow me to continue studying English even if it's difficult, and that it will help me remember what I learn better.

 

이번에는 좀 글을 짧게 써보자.

Me : Let's write down shorter.

Gemini : I'll try to write shorter entries this time.

 

매일 내가 느낀 감정과 생각들을 일기처럼 쓸 것이고, 낭독도 할 것이고, 가능하다면 유튜브에도 올릴 것이다.

Me : Will write down what i've felt and thought today as a diary, speak out to record, and upload it on youtube.

Gemini : I'm going to write down my daily thoughts and feelings like a diary, record myself reading them aloud, and upload them to YouTube if possible.

 

내가 이것으로 기대하는 것은, 내가 주로 사용하는 표현들 위주의 영어 실력 향상과 내 매일의 기록,

그리고 나 자신을 돌보는 시간을 갖고 스트레스를 줄이는 것이다.

Me : What I expect from this works is to increase my English skills with some expressions which I use a lot, record my daily life, and release my stress by writing down my feeling so that I can check myself and do meditate.

Gemini : I hope that this will help me improve my English skills, especially in terms of the expressions I use most often, keep a record of my daily life, take some time for self-care, and reduce stress.

 

꾸준히 진행 한 이후, 기대하는 것들을 얼마나 이뤘는지 보자.

Me : Let's keep up this work and see how many things I will earn.

Gemini : I'll keep track of my progress and see how much I've achieved.

 

영어 문법 검사는 google Gemini, chatGPT 가 진행해줌.

Google Gemini, ChatGPT will check my English.

 

https://youtu.be/tXqAWyWfJ9g?feature=shared

 

 

 

 

 

 

 

 

'English' 카테고리의 다른 글

Study English 24.06.01  (1) 2024.06.01
Study English 24.05.31  (0) 2024.05.31
Study English 23.09.05  (0) 2023.09.19
Study English 23.09.04  (0) 2023.09.19
Study English 23.09.03 구동사 모음9  (0) 2023.09.17

 

AWS EMR Console 에서 직접 step 을 추가하여 shell command 실행하는 방법은 다음과 같음

 

1. EMR Steps 탭에서 Add Step 누름

2. Type 은 "Custom JAR" 선택

3. Name 은 원하는 대로 정함

4. JAR location 은 "command-runner.jar" 입력

5. Arguments - optional 에 bash -c "[원하는 명령어]"  입력

 

예를 들어 echo hello world! 를 실행하려면

bash -c "echo hello world!"

 

ping 8.8.8.8 을 실행하려면

bash -c "ping 8.8.8.8"

 

 

 

 

 


Gradle 은 application 의 빌드와 배포 문제를 해결하기 위한 framework 임
Java 의 경우, java class files, doc files, XML files, property files, 그 외 resources 등
하나의 프로젝트(application)에 굉장히 다양한 파일들을 담고 있음
게다가 각 프로젝트마다 각기 다른 구조 및 버전을 갖고 있어서 빌드하기가 굉장히 까다로움
Gradle 이 이러한 문제 해결에 도움을 줌

Gradle 이 하는 것
- 프로젝트 빌드하고 배포(jar 파일 만들어 줌)
- 외부 lib 의존성 관리

 

Gradle 이 사용하는 build file 의 default 명은 "build.gradle"

Gradle 은 Domain-Specific Language (DSL) 을 정의함 (DSL : 어떤 도메인에서 특정 목적만을 달성하기 위해 사용되는 언어)
예를 들어 프로젝트는 Java 지만, build.gradle 은 Groovy 나 Kotlin 으로 쓰여질 수 있음

Gradle 이 진행하는 작업 단위 하나하나를 "task" 라고 부름
build.gradle 파일을 파싱해서 task 를 뽑아내고 작업을 진행함

task 들은 DAG 를 구성함.
따라서 모든 task 는 실행의 순서가 있으며 반복 실행되지 않음

먼저 실행된 task 의 output 이 나중에 실행된 task 의 input 이 되기도 함

Gradle 은 각 task 의 output 을 저장함
어떤 task 의 input 및 output 이 동일하다면, 다음에 동작할 때 해당 task 는 실행하지 않음으로써 리소스를 아낄 수 있음


Gradle 은 자기 자신을 자동으로 업데이트 할 수 있음
Gradle 은 의존성을 야기하는 lib 들 또한 자동으로 업데이트 할 수 있음


Gradle 의 핵심 구조는 다음과 같음

- Project : 소프트웨어 요소들의 능력(?) 이나 범위를 정의 
- build script : "build.gradle" 이름을 갖는 스크립트 파일. build 하기 위한 지시사항들(task)을 갖추고 있음
- task : 실질적인 작업 사항


hello world 를 프린트하는 굉장히 간단한 build.gradle 예제 (Groovy)

< build.gradle >
task helloWorld {
    doLast {
        println "Hello World"
    }
}

위 내용을 갖는 build.gradle 을 만든 후,
"gradle helloWorld" 명령어를 실행하면, gradle 이 실행되며 Hello World 가 실행됨


"gradle wrapper" 명령어를 실행하면, 기본적인 gradle 구조를 만들어 줌

- build.gradle
- gradle
- gradlew
- gradlew.bat

build.gradle 은 task 들을 모아둔 명령 파일이고, gradlew 로 시작하는 것들은 실행 파일임

각 OS 마다 사용되는 gradle 실행 파일이 다름.
- gradlew : unix(MacOS) 에서 사용 가능한 gradle 실행파일
- graldew.bat : Window 에서 사용 가능
- gradle/wrapper/gradle-wrapper.properties : gradle 버전 정보와 다운로드 url 정보 포함
- gradle/wrapper/gradle-wrapper.jar : gradle wrapper 실행에 필요한 jar 파일

이렇게 생성된 gradlew 를 갖게 되면
MacOS 에서 "./gradlew helloWorld" 같은 명령어 실행이 가능
해당 명령어로 위에서 만든 hello world 출력이 가능함

이렇게 만들어진 gradlew 를 사용하여 빌드하면
내 로컬 환경이 어떻게 구성되었는지 상관없이, 즉 내 컴퓨터에 java 나 gradle 이 설치되지 않아도 빌드가 가능함
다르게 말하면, gradlew 를 다른 컴퓨터나 서버로 옮긴 후에도 빌드가 가능함



참고로 gradlew tasks 명령어를 실행하면
현재 프로젝트에서 사용할 수 있는 gradle tasks 목록을 출력해 줌



Task 에는 여러 종류가 존재함



Ad hoc Task : 단 한 번만 커스텀하게 실행되는 태스크. doFirst, doLast 등의 액션으로 실행됨


task helloWorld {   // 특별한 타입 없이 이렇게 선언 가능하다고 함. 아래 "type"을 갖는 태스크도 존재함
  doLast {  // Action 을 이렇게 선언
    println "Hello World!"
  }
}

실행 할 때는 gradle helloWorld 로 실행함



Type 을 선언하는 Task : copy 등의 타입을 선언한 태스크.
여기서 type 은, "이미 무엇을 할 지 정해져있는 작업모음 함수" 같은 것이라고 생각하자
예를 들어, copy type 은 "files 를 복사하고 dir 에 붙여넣는 작업"내용을 담고 있음
doLast 등의 actions 을 선언 할 필요가 없음. 왜냐하면 type 에서 이미 actions 정보를 갖고 있기 때문

task copyDocs(type: Copy) {
  from "src"
  into "build/docs"
  include "src/**/*md"   // .md 파일만 골라서 copy 하도록 만듦. 여기서 ** 는 특정 dir 와 그 하위 dir 의 모든 파일을 포함하는 wildcard
  includeEmptyDirs = false   // 빈 dir 는 제외하도록 설정
}
task createZip(type: Zip) {
  from "build/docs"   // build/docs 를 압축함
  archiveFileName = "docs.zip"   // 압축파일 이름
  destinationDirectory = file("build/dist")   // 생성된 압축파일이 저장되는 위치
  dependsOn copyDocs   // copyDocs 태스크가 실행된 이후에 createZip 이 실행되도록 의존 관계를 설정
}

실행 할 떄는 gradle copyDocs, gradle createZip 으로 실행함
gradle createZip 을 실행하면, 의존성이 걸려있는 copyDocs 가 같이 실행됨



--dry-run 옵션은 "gradle 실행시 어떤 tasks 를 실행할지 목록"을 보여줌
실제로 tasks 가 실행되지 않음
예를 들면 gradle createZip --dry-run

(혹은 gradle task tree 라는 community plugin 을 사용하여 실행 목록을 확인할 수 있음)



--all 은 gradle 을 실행할 때 동작을 확장하는 역할을 함
예를 들면 gradle tasks --all
gradle dependencies --all   // 모든 의존성 정보를 더 자세하게 출력함(터미널에)




gradle 이 실행(빌드가 실행 되면)되면, 내부적으로 아래 세 가지 스텝(life cycle)을 통해 실행됨.



1. Initialization phase : 빌드 대상이 되는 프로젝트의 정보를 담고 있는 settings gradle file 을 준비함



2. Configuration phase : build script 내 build 로직을 분석함
각 프로젝트마다 build script 를 갖을 수 있지만, 굳이 모든 프로젝트가 젝각각의 build script 를 갖고있을 필요는 없음.
configuration phase 에서는 task 는  실행되지 않지만
(build.gradle 내에 있는) configuration code 는 실행됨
configuration codes 는 task 바깥에 있음. 아래 코드 예제 보면 바로 이해될 것임

// 여기 configuration code 가 존재하면 configuration phase 에서 실행됨
task helloWorld {
  // 여기 configuration code 가 존재하면configuration phase 에서 실행됨
  doFirst {}
  doLast {}
}



3. Execution phase : tasks 를 기준으로 생성된 DAG 따라서 순서대로 tasks 를 실행함
DAG 는 메모리 위에 있다고 함
execution phase 에서 실행하는 code 는 task 안쪽에 있음.

task helloWorld {
  doFirst {
    // 여기 execution code 가 존재하면 execution phase 에서 실행됨
  }
  doLast{
    // 여기 execution code 가 존재하면 execution phase 에서 실행됨
  }
}



여러 프로젝트의 build.gradle 내에 동일 코드가 반복되어 쓰여지는 것을 방지하고
중복 코드를 효율적으로 관리하기 위해
plugin 이라는 기능을 제공함.



예를 들어보자

< archiving.gradle >
task helloWorld {
 doLast{
    println "Hello World!"
  }
}

위 archiving.gradle 을 아래 build.gradle 에서 가져올 수 있음

< build.gradle >
apply from: "archiving.gradle"

그러면 build.gradle 내에 helloWorld task 가 들어가게 되어, 아래처럼 gradle 명령어 실행이 가능함

gradle helloWorld



gradle 에서 제공하는 "base" 라는 plugin 을 사용할 수 있음
base plugin 은 유용한 빌드 기능을 제공함
예를 들어
- gradle clean : 정리 작업
- gradle assemble : 프로젝트 산출물을 jar 로 패키징 하는 작업
- gradle check : 프로젝트 테스트 실행하는 작업

plugin 추가는 아래처럼 하면 됨

< build.gradle >
apply plugin: "base"
apply from: "archiving.gradle"

혹은

plugins {
  id 'base'
}



Domain Object 는 프로젝트 구성 요소를 추상화한 객체임
Domain Object 는 Gradle 이 프로젝트를 구성하고 빌드를 실행하는 데 필요한 모든 주요 객체를 의미함
build.gradle 내에는 아래와 같은 Domain Object 들이 존재함



Project : Gradle 빌드의 최상위 도메인 객체임
하나의 프로젝트는 여러 서브 프로젝트를 포함할 수 있으며
각 프로젝트에는 자신만의 tasks, plugin, dependencies,configuration 이 존재함

build.gradle 내에서 아래처럼 쓰여짐

project.name = "EyeballsProject"



Task : Gradle 빌드에서 실행되는 작업들을 나타내는 객체임
tasks 는 빌드의 구체적인 단계를 정의하며, 컴파일이나 테스트, 패키징 등을 수행함

build.gradle 내에서 아래처럼 쓰여짐

task helloWorld {
  doLast {
    println "Hello World!"
  }
}



Configuration : 의존성 관리를 위한 configuration 을 나타내는 객체임
gradle 이 빌드하고 배포하는 대상 프로젝트 내에서 사용되는 라이브러리나 모듈을 정의함

build.gradle 내에서 아래처럼 쓰여짐

configurations {
  implementation ....
}



Repository : 외부 의존성(dependencies)을 가져올 때 사용하는 저장소를 나타냄
Maven, Ivy, Gradle Plugin Portal 등의 저장소 정의 가능

build.gradle 내에서 아래처럼 쓰여짐

repositories {
  mavenCentral()
}



SourceSet : 소스 코드와 그에 관련된 자원을 관련된 자원을 관리하는 객체임
각 sourceSets 은 특정 컴파일러와 설정과 함께 소스코드 파일과 리소스 파일을 정의함

build.gradle 내에서 아래처럼 쓰임

sourceSets {
  main {
    java {
      srcDirs = ['src/main/java']
    }
  }
}



gradle 에서 제공하는 "java plugin" 의 핵심 기능은 컴파일링(빌드), 테스팅, 그리고 번들링(배포)임
빌드는 java, scala 코드 class 들을 빌드해주고
배포는 class 들을 jar 파일로 아카이빙 해주는 것

만약 gradle 이 없었다면, 직접 javac 명령어와 jar 명령어를 통해 빌드하고 배포했을 것임
이러한 명령어 사용 대신, java plugin 의 기능을 이용하여 빌드 및 배포 가능




java plugin 을 사용하기 위해서, java plugin 에 필요한 코드 구조를 충족해줘야 함
아래와 같은 구조를 갖추면, java plugin 이 알아서 이를 인지하고
test, compile, bundle 작업에 사용한다고 함

-src
ㄴ main
    ㄴ java : production source code 가 포함된 dir
    ㄴ resources : 런타임에 사용되는 리소스들이 포함된 dir
ㄴ test
    ㄴ java : src/main/java 를 테스트하기 위한 code 가 포함된 dir
    ㄴ resources : 테스트에 사용되는 리소스들이 포함된 dir



java plugin 이 작업을 마친 후 생성한 결과들(classes, jar) 은 아래에 위치하게 됨

-build
ㄴ classes : java plugin 이 compile 한 class 파일들이 저장되는 dir
ㄴ libs : java plugin 이 생성한 jar 파일이 저장되는 dir
ㄴ resources 





build.gradle 은 최상위 root dir 에 위치시키면 됨
build.gradle 내에 아래와 같이 추가하면 됨

plugins {
  id 'java'
}


build.gradle 위치
https://docs.gradle.org/current/userguide/java_plugin.html

 

 

 


java plugin 의 명령어 중 compileJava 는
src/main/java 에 위치한 production source code 를 컴파일하는 작업을 진행함

java compile 가 실행 가능하도록 PATH 환경 변수 설정이 되어있어야 하며
컴파일 후 생성되는 바이너리 파일들(class 파일들)은 build/classes 에 저장됨

아래처럼 complieJava 명령어 실행

./gradlew compileJava --console=verbose



java plugin 의 명령어 중 processResources 는
src/main/resources 에 위치한 리소스 파일들을 build/resources/main dir 에 옮기는(copy) 작업을 진행함

추후 jar 파일을 만들 때 build dir 아래 있는 것들을 jar 로 만드는데
이 때 리소스 파일들까지 함께 jar 에 넣기 위해
위와 같은 작업을 하는 것임



java plugin 의 명령어 중 classes 는
위에서 실행한 두 명령어( compileJava  , processResources ) 를 한 번에 실행하는 작업임



java plugin 이 컴파일 할 때 자바 버전을 직접 지정할 수 있음
그리고 compile 하다가 이슈가 발생하면 어떻게 하고 싶은지도 지정 가능
이를테면 나는 JAVA 11 버전을 사용하고 싶다! 그러면 build.gradle 을 아래와 같이 설정하면 됨

plugins {
  id 'java'
}

java {
  sourceCompatibility = JavaVersion.VERSION_11   // compile 할 production java code 의 버전
  targetCompatibility = JavaVersion.VERSION_11   // 앞으로 생성될 class 파일이 java 11 버전에서 실행할 수 있도록 설정
}

compileJava {
  options.compilerArgs << '-Werror'
}




아래는 sourceCompatibility targetCompatibility 의 차이를 chatgpt 에게 물어본 결과임

Gradle에서 sourceCompatibility와 targetCompatibility는 Java 컴파일러가 소스 코드와 바이트코드를 처리하는 방식을 제어하는 속성입니다. 이 두 속성은 컴파일 시 호환성을 설정하는 데 사용되며, 각각의 의미는 다음과 같습니다.

1. sourceCompatibility

의미: 소스 코드가 어떤 버전의 Java 문법과 API를 사용할 수 있는지를 정의합니다.

설정하는 값: 이 값은 소스 코드를 컴파일할 때, 컴파일러가 해당 Java 버전에서 허용된 문법과 API를 사용하도록 제한합니다.

사용 예시: 만약 sourceCompatibility = '1.8'으로 설정하면, 소스 코드는 Java 8의 문법과 API를 사용할 수 있습니다. 그 이후 버전(Java 9, Java 10 등)의 기능을 사용하려고 하면 컴파일 오류가 발생합니다.

sourceCompatibility = '1.8'


2. targetCompatibility

의미: 컴파일된 바이트코드가 어떤 Java 런타임 버전에서 실행될 수 있는지를 정의합니다.

설정하는 값: 이 값은 컴파일된 바이트코드가 어떤 버전의 JVM에서 실행될 수 있는지를 결정합니다. 즉, 컴파일 결과물이 해당 버전의 JVM과 호환되는 바이트코드로 변환됩니다.

사용 예시: targetCompatibility = '1.8'으로 설정하면, 컴파일된 바이트코드는 Java 8 버전의 JVM에서 실행할 수 있게 컴파일됩니다. 만약 이 값을 Java 7으로 설정하면, 더 낮은 버전의 JVM에서도 실행할 수 있게 바이트코드를 생성하지만, 소스 코드의 문법이나 API는 여전히 sourceCompatibility에 의해 제한됩니다.

targetCompatibility = '1.8'


주요 차이점

**sourceCompatibility**는 소스 코드 레벨에서 사용할 수 있는 Java 버전을 설정합니다. 즉, 어떤 Java 문법과 API를 사용할 수 있을지를 결정합니다.

**targetCompatibility**는 바이트코드 레벨에서 어떤 Java 버전과 호환될지를 설정합니다. 즉, 컴파일된 클래스 파일이 어떤 JVM에서 실행될 수 있는지를 결정합니다.


실제 사용 시나리오

1. 같은 버전으로 설정: 대부분의 경우, sourceCompatibility와 targetCompatibility는 같은 버전으로 설정됩니다. 예를 들어, Java 8을 대상으로 하고, 그 버전에서 실행되도록 만들 때:

sourceCompatibility = '1.8'
targetCompatibility = '1.8'


2. 호환성을 위해 다른 버전으로 설정: 예를 들어, 최신 Java 문법을 사용하지만, 더 오래된 JVM에서 실행할 수 있는 코드를 생성하고 싶다면 sourceCompatibility를 최신 버전으로, targetCompatibility를 더 낮은 버전으로 설정할 수 있습니다.

Java 11 문법을 사용하고, Java 8 JVM에서 실행되도록 설정:


sourceCompatibility = '11'
targetCompatibility = '1.8'

이 설정은 Java 11의 문법을 사용하되, 최종적으로 Java 8에서 실행 가능한 바이트코드로 컴파일됩니다. 단, 이 경우 Java 8에서 존재하지 않는 API를 사용하려고 하면 런타임 오류가 발생할 수 있습니다.



요약

sourceCompatibility: 소스 코드가 사용할 수 있는 Java 문법과 API를 정의.

targetCompatibility: 컴파일된 바이트코드가 실행될 JVM 버전을 정의.


이 두 설정을 적절히 사용하면, 특정 Java 버전에서 코드를 작성하고, 더 넓은 범위의 JVM에서 실행되도록 조정할 수 있습니다.





--console=verbose 옵션이 붙으면, 빌드 실행 내용을 콘솔에 더 자세하게 출력함
--console=plain 옵션이 붙으면 단순한 텍스트 모드로 출력함
--console=rich 옵션이 붙으면 콘솔에 컬러 및 애니메이션 적용하여 빌드 진행 상태를 시각적으로 출력함. 이게 default 값임
--console=auto 옵션이 붙으면 콘솔 환경에 따라 rich 혹은 plain 으로 자동 설정됨



jar 파일 (java archive 파일) 을 생성하는 jar 명령어가 실행될 때
만약 compile 되지 않아서 class 파일이 존재하지 않는다면,
자동으로 compile 명령어를 실행하고 jar 파일을 만든다고 함
(굳이 직접 compile 명령어( compileJava  , processResources , classes )를 실행하지 않아도 된다는 말인 듯)

jar 명령어는 아래처럼 사용 가능

./gradlew jar

이렇게 생성된 jar 파일은 build/lib dir 에 생성됨


생성된 jar 파일 이름은 01_01.jar 처럼 의미없이 생성됨 
우리가 아래처럼 build.gradle 에 jar 를 추가함으로써, 직접 이름을 지정할 수 있음

jar {
  archiveBaseName = 'eyeballs'
}

그럼 build/lib/eyeballs.jar 가 생성됨



아래처럼 jar 파일에 버전도 추가 가능

version = '1.0.0'
jar {
  archiveBaseName = 'eyeballs'
}

그럼 build/lib/eyeballs-1.0.0.jar 가 생성됨



build.gradle 에 application plugin 을 추가하면
production source code 를 간단하게 실행해줌

plugins {
  id 'application'
}
application {
  mainClass = 'com.tistory.eyeballs.Main'
}

./gradlew run

만약 실행할 때 argument 도 같이 넣어 실행하고 싶다면 run 명령어 뒤에 --args 를 붙이면 됨

./gradlew run --args="2024 eyeballs"



Gradle Dependency Management Engine 은 maven 같은 repository 로부터 lib 를 다운받고 프로젝트에 적용함
빌드하는 동안  lib(dependencies) 를 다운받고, local cache 에 저장하고
project 의 classpath 에 추가하여 project 에서 해당 lib 를 사용할 수 있도록 함

또한, 서로 의존성이 있는 다양한 modules 로 구성된 프로젝트에서
modules 간 의존성을 설정할 수 있음.
이를 multi-project build 라고 부름

또한, artifact 를 생성하고 repository 에 등록하여,
다른 end user 가 다운받아 사용할 수 있도록 만들 수 있음
이를 publishing 이라고 함



dependencies 는 build.gradle 에 아래와 같이 등록 가능함
예를 들어 maven repo 로부터 commons-cli:commons-cli:1.4 라는 lib 를 다운받고 프로젝트에 추가한다고 하자

repositories {
  mavenCentral()   // maven repo 를 등록하여, 다운받을 lib 를 찾을 수 있도록 함
}

dependencies {
  implementation 'commons-cli:commons-cli:1.4'   // 다운받아 프로젝트에 적용할 lib 를 여기 추가
}



참고로 repo 에 들어있는 lib 의 형태는 아래와 같이 group, artifact, version 세 가지 정보로 이루어져 있음



Gradle 에 dependencies 를 계속 추가하여 복잡해져도,
"dependencies task" 를 이용하여 한 눈에 tree 형태로 dependencies 를 볼 수 있음

아래와 같은 명령어로 실행하면 됨

./gradlew dependencies



이 dependency 가 왜 필요한지, 어디서 왔는지 등을 알고 싶다면
"dependencyInsight task" 를 실행하여 확인할 수 있음

아래와 같은 명령어로 실행하면 됨

./gradlew dependencyInsight


" dependencyReport task" 도 사용해보자

./gradlew dependencyReport



app 과 api 라는 두 개의 프로젝트 존재하고,
app 은 api 의 class 를 사용하는, 즉 api 에 의존성을 갖는 프로젝트라고 하자

이런 상황에서 gradle 이 이슈 없이 app 을 빌드하려면 어떻게 해야 할까?
gradle 통해 app 과 api 프로젝트들의 서로 간 의존성을 관리하고 빌드하면 됨
이를 multi project build 라고 함




root dir 에 위치한 settings.gradle 에 아래와 같이 추가함

< settings.gradle >
rootProject.name = 'eyeballsProject'
include ':app', ':api'   // app, api 두 프로젝트를 나의 'eyeballsProject' 의 하위 프로젝트로 추가함


그리고 프로젝트의 하위 프로젝트들을 표시하는 명령어인 project task 를 실행하여
잘 추가되었는지 확인함

./gradlew projects

결과 : 
Root project 'eyeballsProject'
+--- Project ':api'
\--- Project ':app'



app 이 빌드되기 위해선 app 에서 api 의 class 를 import 할 수 있어야 함
이를 위해, 아래와 같이 app/build.gradle 의 dependencies 에 api 프로젝트를 추가해 줌


< app/build.gradle >

dependencies {
  implementation project(':api')
}

그리고 전체 프로젝트르 빌드하면 app 과 api 프로젝트 간 의존성이 해소되어 문제 없이 빌드됨

./gradlew classes



추가로, multi project build 를 위해
하위 프로젝트들을 include 로 추가하면 얻는 이점에 대해
Gemini 에게 물어본 결과는 아래와 같음


include 키워드를 사용하여 하위 프로젝트를 추가하는 가장 큰 이유는 다음과 같습니다.

1. 멀티 모듈 프로젝트 관리:
하나의 루트 프로젝트: 여러 개의 관련된 모듈을 하나의 루트 프로젝트 아래에서 관리할 수 있습니다. 이는 프로젝트의 복잡성을 줄이고 유지보수를 용이하게 합니다.
모듈 간 의존성 관리: 각 모듈은 독립적으로 개발하고 테스트할 수 있으며, 필요한 경우 다른 모듈에 의존하도록 설정할 수 있습니다.
2. 모듈별 빌드 설정:
모듈별 설정: 각 모듈마다 다른 빌드 설정을 적용할 수 있습니다. 예를 들어, 특정 모듈은 Java로 개발되고 다른 모듈은 Kotlin으로 개발될 수 있습니다.
유연한 빌드: 다양한 유형의 모듈을 하나의 프로젝트에서 관리할 수 있어 빌드 프로세스를 유연하게 구성할 수 있습니다.
3. 코드 재사용:
공통 모듈: 여러 프로젝트에서 공통적으로 사용되는 코드를 별도의 모듈로 분리하여 관리하고 재사용할 수 있습니다.
코드 중복 방지: 코드 중복을 줄이고 유지보수 비용을 절감할 수 있습니다.
4. 모듈별 테스트:
모듈별 테스트: 각 모듈에 대한 단위 테스트를 수행하고, 통합 테스트를 통해 모듈 간의 상호 작용을 검증할 수 있습니다.
테스트 효율성 향상: 모듈별로 테스트를 수행하여 테스트 커버리지를 높이고 테스트 시간을 단축할 수 있습니다.
5. 모듈별 배포:
모듈별 배포: 각 모듈을 독립적으로 배포할 수 있습니다.
배포 유연성: 필요에 따라 특정 모듈만 배포하거나, 여러 모듈을 함께 배포할 수 있습니다.



./gradlew installDist 명령어는, Gradle 프로젝트를 독립 실행 가능한 배포판으로 만드는 명령
해당 프로젝트를 다른 환경에서 별도의 Gradle 설치 없이 실행할 수 있도록
모든 필요한 파일과 설정을 하나의 디렉토리에 패키징하는 작업을 수행함

해당 명령어가 실행되면 프로젝트의 최상위 디렉토리에 build/install 디렉토리가 생성되고
그 안에 완성된 배포판이 포함됨

해당 명령어를 통해 생성되는 파일 구조
- bin 디렉토리: 프로젝트를 실행하는 데 필요한 실행 스크립트 (예: gradlew, gradlew.bat)가 포함됨
- lib 디렉토리: 프로젝트에서 사용하는 모든 라이브러리 파일이 포함됨
- conf 디렉토리: Gradle 설정 파일이 포함됨
- resources 디렉토리: 프로젝트의 리소스 파일이 포함됨




JUnit 은 많은 인정을 받는 표준 java test framework 임
Gradle Java plugin 은 test source code 를 찾아 실행(테스팅)함
예를 들어 아래와 같은 dir 를 갖춘 프로젝트에서 src/test/java/*.java 를 테스트 할 수 있음

src
ㄴmain
   ㄴjava
      ㄴMyClass.java
ㄴtest
   ㄴjava
      ㄴMyClassTest.java



test 를 위한 lib 인 Junit 을 dependencies 에 추가할때는 testImplementation configuration 을 사용함
testImpelentation 은 테스트 코드를 컴파일하고 실행하는 데 직접적으로 필요한 의존성을 지정.

testRuntime configuration 은 테스트 실행 시(Runtime)에만 필요한 의존성을 지정.
테스트 코드를 컴파일 할 때 필요하진 않음
하지만 테스트 실행 시에 필요한 추가적인 의존성을 설정할 수 있음

이렇게 test configuration 으로 추가된 lib 는 나중에 production 을 위한 배포파일을 만들 때, 배포 파일에 추가되지 않음
그리고 오로지 test code 만을 위해 사용됨, production source code 에는 적용되지 않음


< build.gradle >

dependencies {
  testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
  testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}

test {
  useJunitPlatform()
  testLogging {
    events 'started', 'skipped', 'failed'
  }
}

여기서 junit-jupiter-api 는, 개발자가 테스트 코드를 작성할 때 사용하는 API를 제공하는 lib
예를 들어 @BeforeEach, @AfterEach 등의 애노테이션을 제공한다거나, 
다양한 assertion 메소드를 제공함

junit-jupiter-engine 는, 실제로 테스트를 실행하고 결과를 만드는 lib
@Test 애노테이션이 달린 메소드를 찾아 테스트 대상으로 지정하고 테스트를 실행

useJunitPlatform() 은 빌드 프로세스에서 JUnit Platform을 테스트 실행 엔진으로 사용하겠다는 의미
Gradle의 'test' task 에 JUnit Platform을 설정하는 메소드이며
이 설정을 통해 Gradle은 프로젝트에서 JUnit 5 스타일의 테스트를 실행할 수 있도록 환경이 구성됨

testLogging 은 테스트 실행 시 출력되는 로그의 상세함과 내용을 조절하는 설정임
즉, 테스트 결과를 얼마나 자세하게 보여줄지를 설정
테스트를 시작할 때(started), 테스트가 실패했을 때(failed) 발생하는 이벤트를 콘솔로 출력해달라는 의미임



MyClassTest.java 는 아래와 같은 내용으로 구성 가능

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class MyClassTest {
  private final MyClass myClass = new MyClass();

  @Test
  void test() {
    assertEquals("eyeballs", myClass.getName());
  }
}



test 코드를 생성한 이후에 실제로 Test 를 하기 위해
compileTestJava 명령어로 테스트 코드를 compile 함

./gradlew compileTestJava

그리고 test 명령어로 테스트 코드 실행

./gradlew test

test 를 마치고나면, JUnit 이 HTML 및 XML 로 리포팅 페이지를 만들어 줌
HTML 파일은 build/reports/tests/test/index.html 에 위치하며, 열어보면 테스트 결과를 볼 수 있음
XML 파일은 build/test-results/test/*.xml 에 위치하며, 열어보면 테스트 결과를 볼 수 있음



추가로, testImplementation 과 testRuntime 의 차이에 대해 Gemini 에게 물어본 결과

언제 어떤 configuration을 사용해야 할까요?

testImplementation:

  테스트 코드에서 직접 사용하는 의존성 (테스트 더블, 테스트 데이터 생성 라이브러리 등)
  테스트 코드 컴파일 시 필요한 의존성
testRuntime:
  테스트 실행 환경을 구축하는 데 필요한 의존성 (임베디드 DB, 웹 서버 등)
  테스트 코드 컴파일에는 필요하지 않지만, 테스트 실행 시에만 필요한 의존성

왜 구분하여 사용해야 할까요?


테스트 코드의 독립성: 테스트 코드를 생산 코드와 분리하여 관리하고, 테스트 환경에 필요한 의존성만 포함시킴으로써 테스트 코드의 독립성을 보장할 수 있습니다.

빌드 시간 단축: 테스트 실행에만 필요한 의존성은 컴파일 시에 포함되지 않으므로 빌드 시간을 단축할 수 있습니다.
의존성 관리: 테스트 환경에 필요한 의존성을 명확하게 관리하여 의존성 충돌을 방지하고 프로젝트를 안정적으로 유지할 수 있습니다.



 

 

 

 

 

 

 

 

< First-Class 함수 >

 

First-Class 함수 : 프로그래밍 언어가 함수(Function)를 first-class 시민으로 취급하는 것

함수가 다른 함수의 인자로 전달될 수 있고, 함수의 결과로 리턴될 수 있고,

변수에 함수를 할당하거나 list 등의 구조 안에 함수를 할당하는 것이 가능하다는 말임

 

 

 

< closures >

 

closure : 자신의 영역 밖에서 호출된 함수의 변수값과 레퍼런스를 복사하고 저장한 뒤,

  이 캡처한 값들에 액세스할 수 있게 도와줌

free variable : 코드블럭안에서 사용은 되었지만, 그 코드블럭안에서 정의되지 않은 변수

def outer_func():
    message = 'Hi'

    def inner_func():
        print(message)

    return inner_func

my_func = outer_func()

my_func()  # Hi 가 출력됨

 

my_func 에서 __closure__ 라는 속성을 확인할 수 있으며, __closuer__ 는 tuple 임

__closuer__ 에는 cell 이라는 요소가 포함되어있으며

이 cell.cell_contents 를 열어보면 "Hi" 라는 글자가 있음

 

클로저는 지역 변수와 코드를 묶어서 사용하고 싶을 때 활용

클로저에 속한 지역 변수는 바깥에서 직접 접근할 수 없으므로 데이터를 숨기고 싶을 때 활용

 

 

 

 

 

 

< Iterator, Genrator, Decorator >

 

- Iterator : 요소가 복수인 컨테이너 타입 객체들(list, tuple, set, dironary, string)에서

  각 요소를 하나씩 꺼내 반복적으로 처리할 수 있도록 하는 방법을 제공하는 객체

  list, tuple 등의 객체들이 갖는 __iter__() 함수를 통해 Iterator 구현이 가능함

mytuple = (1,2,3)
myit = iter(mytuple)

print(next(myit))  #1
print(next(myit))  #2
print(next(myit))  #3

 

 

- Generator : Iterator 의 한 종류이며, 메모리를 효율적으로 사용하면서 반복을 수행하도록 돕는 객체

  미리 요소를 리스트에 만들어 놓고 꺼내는 것이 아니라,

  yield 및 __next__() 함수를 통해서 필요할 때마다 접근/생성하여 요소를 빼냄

def yield_test():
    for i in range(5):
        yield i
        print(i,'번째 호출!')

print(type(yield_test())) # <class 'generator'> yield 가 사용되면 generator 가 되나 봄

t = yield_test()
print(t.__next__()) # 0
print(t.__next__()) # 0 번째 호출! 1
print(t.__next__()) # 1 번째 호출! 2
print(t.__next__()) # 2 번째 호출! 3
print(t.__next__()) # 3 번째 호출! 4
#print(t.__next__()) #Error

 

  함수 내에서 yield 함수를 사용하여 값을 지정해두면,

  추후 __next__() 를 통해 값을 불러올 수 있음

  __next__() 함수가 실행되면, 내부에서 yield 를 실행하고 멈추며, 다시 실행하면 멈춘 곳부터 다시 실행

  yield 키워드 위치에서 함수가 끝나지 않은 유휴 상태로 대기

  이렇게 generator 를 사용하면, 필요할 때 마다 해당 객체를 통해 요소를 반환할 수 있음

  함수를 완전 실행시키는 것이 아니라, 일부를 실행시키고 일시 정지하기 때문에, 함수의 재사용성이 높아지고

  전체 요소를 메모리에 저장할 필요가 없어 비용효율적

 

- Decorator : 기존의 코드에 여러가지 기능을 덧붙여 실행하는 파이썬 구문

  이미 만들어져 있는 기존의 코드를 수정하지 않고

  (기존 코드와 새로  추가한 코드가 혼합된 래퍼(wrapper) 함수를 이용하여) 여러가지 기능을 앞뒤에 추가하기 위해 사용함

  대개 로그를 남기는데 사용되거나, 프로그램의 성능 테스트(실행 시간 측정)하기 위해서도 많이 쓰임

  기존 코드 중간에 뭔가 넣는 작업은 할 수 없고,

  단지 작업의 앞 뒤에 추가적인 작업을 손쉽게 넣어 사용하도록 하는 역할만 함

 

def decorator_function(original_function):
    def wrapper_function():
        print('{} 함수가 호출되기전 입니다.'.format(original_function.__name__)) # 꾸미고 싶어 추가한 부분
        return original_function()

    return wrapper_function


def display_1(): #수정하고 싶지 않은 함수 display_1
    print('display_1 함수가 실행됐습니다.')

display_1 = decorator_function(display_1) # display_1 함수를 인자로 넘기고 래퍼함수를 받음

display_1()

 

일반적으로는 @ 를 사용하여 데코레이터 함수와 연결한다고 함

def decorator_function(original_function):  # original_function 에 display_1 이 들어감
    def wrapper_function():
        print('{} 함수가 호출되기전 입니다.'.format(original_function.__name__))
        return original_function()

    return wrapper_function


@decorator_function
def display_1():  # 꾸미고 싶은 대상. display_1 이 decorator_function 의 인자( original_function )로 들어감
    print('display_1 함수가 실행됐습니다.')

# display_1 = decorator_function(display_1)

display_1()

 

 

  복수의 데코레이터를 동시에 사용하면, 아래쪽 데코레이터부터 실행되는데

  첫번째 데코레이터를 통해 리턴받은 래퍼 함수 대상으로 다시 두번째 데코레이터가 실행됨

  import functools.wraps 의 wraps 를 (데코레이터 내부의) 래퍼 함수에 @ 로 걸어줌

 

 

 

 

 

 

 

< REPL >

REPL : Read-Eval-Print-Loop

인터프리터 언어인 python 는 REPL 이 기본

 

 

 

 

< magic method >

 

매직 메소드란, 클래스안에 정의할 수 있는 스페셜 메소드.

클래스를 (int, str, list 등의) 파이썬 빌트인 타입(built-in type)과 같이 작동하도록 만들어 줌

 

클래스를 만들때 항상 사용하는 __init__이나 __str__는 가장 대표적인 매직 메소드

__init__ 메소드는 class 의 instance 를 생성할 때 자동으로 실행됨

 

int(3)+2 를 실행하면 5가 나오는데, 그 이유는 '+' 가 매직메소드인 __add__() 를 호출하여

int(3).__add__(2) 를 실행하기 때문

 

직접 만든 class 의 +, -, <, init 등의 매직 메소드들을 커스터마이징하여

원하는 기능대로 동작할 수 있도록 만들면 편함

 

 

 

< 클래스 변수 >

 

클래스의 인스턴스가 아닌, 클래스 자체에 붙어있는 변수

class MyClass:
    class_var = "Hi!"
    def __init__(self):
        print(MyClass.class_var)
        print(self.class_var)

my_class = MyClass()

 

self.class_var 로 접근 가능함

python 은 네임스페이스를 찾을 때 아래와 같은 순서로 찾기 때문.

인스턴스 네임스페이스(self.class_var) 로 찾아서 없으면

바로 위 클래스 네임스페이스(MyClass.class_var) 로 찾음

 

 

__init__ 함수를 통해 인스턴스 변수를 설정하는 방법 외에

아래와 같이 직접 인스턴스를 통해 변수를 집어넣는 방법도 있음

 

클래스 변수는 해당 클래스의 인스턴스들끼리 공유하는 전역변수처럼 사용이 가능......

 

...그럼 클래스 변수는 메모리에 올라와 있다는 말이 됨

 

 

 

< 인스턴스 메소드, 클래스 메소드, 스태틱 메소드 >

 

인스턴스 메소드 : 인스턴스를 통해서만 호출이 됨

  인스턴스 메소드의 첫 번째 인자로 인스턴스 자신을 자동으로 전달

  관습적으로 이 인수를 ‘self’라고 칭함

 

클래스 메소드 : 클래스 변수와 마찬가지로, 클래스 내 모든 인스턴스가 공유 가능한 메소드

  클래스 메소드는 ‘cls’인 클래스를 인자로 받고,

  (모든 인스턴스가 공유하는 클래스 변수 같은) 데이터를 생성, 변경 또는 참조하기 위한 메소드

< 클래스 메소드를 사용하지 않은 버전 > < 클래스 메소드를 사용한 버전 >

 

  클래스 메소드에서 사용되는 cls 는 클래스 자기 자신을 의미함.

  따라서 위의 오른쪽 예제처럼, 클래스 메소드를 호출함으로서 (클래스를 리턴받아 ) 클래스를 만드는 게 가능

  팩토리 메소드 역할을 함

 

  클래스 변수를 업데이트해야 한다면, 클래스 메소드를 이용해서 업데이트하는 게 좋다고 함

  데이터 검사나 다른 부가 기능 추가가 용이해서

 

스태틱 메소드 : 위의 두 메소드와는 다르게, 인스턴스나 클래스를 첫 번째 인자로 받지 않음

  스태틱 메소드는 클래스 안에 정의되어, 클래스 네임스페이스 안에 있을뿐 

  일반 함수와 전혀 다를게 없음

  클래스와 연관성이 있는 함수를 클래스 안에 정의해두고

  클래스나 인스턴스를 통해서 호출하여 편하게 사용

 

 

staticmethod 인 func_a 와 func_b 를 통해

staticmethod 에서는 self 에 접근이 불가능하다는 것과

staticmethod 는 클래스 및 인스턴스를 통해 호출 가능하다는 것을 확인

추가로 staticmethod 는 클래스 변수에도 접근 가능

자바의 static method 와 동일하다고 보면 될 듯

 

staticmethod 는 클래스와 연관된 순수함수를 설정할 때 사용하고,

classmethod 는 (위 예제처럼) 팩토리 메소드로 사용하거나, 클래스 변수를 변경할 때 사용

(그렇다고 staticmethod 가 클래스 변수를 변경하지 못하는 건 아님)

 

 

 

 

 

< 클래스 속성(인스턴스 변수들) 확인하는 방법 >

 

__dict__ 를 사용하여 확인 가능

 

위 예제에서 볼 수 있듯이

클래스 변수는 보이지 않고 인스턴스 변수만 확인 가능

dir() 는 해당 객체로 사용 가능한 내장 함수 리스트를 보여줌

 

 

 

 

< 오버라이딩에서 사용하는 super >

 

부모 클래스의 메소드를 호출할 때 사용하는 super 에 자기 자신의 클래스를 넣어줘야 함....

안 그러면 에러가 남

 

 

 

 

 

< underscore(_) 사용법 >

 

1. Python Interpreter 에서 마지막에 실행된 결과값으로 사용

>>> 10
10
>>> _
10
>>> _ * 3
30

 

2. 값을 무시하고 싶을 때 사용

x, _, y = (1, 2, 3) # x = 1, y = 3
x, *_, y = (1, 2, 3, 4, 5) # x = 1, y = 5

for _ in range(1, 4):
    print(_)  # 1,2,3 이 출력됨

 

3. private 으로 만들고 싶을 때 사용

가령 _로 시작하는 변수와 메소드를 갖는 모듈을 from module import * 로 임포트 한다면

_ 로 시작하는 변수, 메소드는 임포트에서 무시됨

(하지만 직접 module._... 을 통해 접근은 가능하여, 진정한 private 접근 제어는 아님)

def __init__(self):
    pass

_a = "a"  #private 변수. import * 에서 제외됨

b = "b"

def _echo(self, x):  #private 메소드. import * 에서 제외됨
    print x

def func(self):
    print("eyeballs")

 

4. 매직메서드에 사용

__init__, __len__ 등

__file__ 은 현재 파이썬 파일의 위치를 나타내며

__eq__ 은 'a == b' 같은 식이 수행될 때 호출되는 메소드

 

5. 맹글링 규칙을 이용하여 오버라이드를 피하기 위해 사용

맹글링이란, 컴파일러나 인터프리터가 변수/함수명을 그대로 사용하지 않고

일정한 규칙에 의해 변형시키는 것을 의미함

파이썬의 맹글링 규칙중에는 다음과 같은 규칙이 있음

"속성명이 double underscore (__) 로 시작한다면, 이때 속성명 앞에 _ClassName 을 붙임"

class MyClass:
    a = "a"
    __d = "d"  #맹글링 대상
    def b(self):
         pass
    def __c(self):  #맹글링 대상
         pass

dir(MyClass())
['_MyClass__c', '_MyClass__d', ... 'a', 'b']

 

이게 어디서 유용한가?

상속받은 자식클래스에서 동일한 이름의 변수/함수명을 사용하고 싶은데, 오버라이딩은 하기 싫을 때 사용함.

 

 

 

 

< Asterisk(*) 이해하기 >

 

1. 곱셈(2*3 = 6) 및 제곱(2**3 = 8) 에 사용

2. 리스트형 컨테이너 타입의 반복 확장에 사용

>>> zero_list = [0]*10
>>> zero_list
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> zero_tuple = (0,)*10
>>> zero_tuple
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

 

3. 가변인자 (Variadic Parameters)에 사용

packing : args, kwargs 를 통해 컨테이너 타입 형태로 데이터(가변인자)를 받는 것

 

*args : positional arguments 를 받음. arguments 몇개 받는지 모르는 경우 사용. 튜플 형태로 넘어옴

 

**kwargs : keyword argument 의 약자. keyword arguments 를 받음

  args 와 동일한 역할을 하며 key, value 를 함께 전달해야 함. key value 형태가 아니면 에러가 남

  dict 형태로 넘어옴

 

두 가지 모두 함께 쓸 수 있음. args 가 먼저 와야하며, 함수 사용할때도 args 인자가 먼저 와야 함

 

4. 컨테이너 타입 데이터(list, tuple, dict) 를 unpacking 할 때 사용
unpacking : 컨테이너 타입 데이터를 (가변인자나 변수에 넣기 위해) 앞에 * 를 붙이는 것

 

또한 리스트 혹은 튜플을 unpacking 한 후 다른 변수에 packing 하여 넣기도 함

numbers = [1, 2, 3, 4, 5, 6]

# unpacking의 좌변은 리스트 또는 튜플의 형태를 가져야하므로 단일 unpacking의 경우 *a가 아닌 *a,를 사용
*a, = numbers
# a = [1, 2, 3, 4, 5, 6]

*a, b = numbers
# a = [1, 2, 3, 4, 5]
# b = 6

a, *b, = numbers
# a = 1
# b = [2, 3, 4, 5, 6]

a, *b, c = numbers
# a = 1
# b = [2, 3, 4, 5]
# c = 6

 

 

 

 

 

 

< print 대신 logging 을 사용하는 이유 >

 

1. 로깅 레벨 조정 가능 (debug, info, warning, error, critical)

2. stdout 뿐 아니라 file 로 로그를 저장할 수 있음

3. 로깅 포맷 편집 가능 (파일 이름, 라인 넘버, 로그 메세지, 프로세스/스레드 이름, 시간 등)

4. 시간에 따라 로그 파일 생성 가능. 가령 5분 단위로 로깅하여 파일을 생성

 

 

 

 

 

< Trailing comma >

리스트나 튜플을 만들 때 가장 마지막에 넣는 comma ( , )

사람의 실수를 방지하기 위해 넣음

 

 

 

< Comprehension >

Comprehension : iterable한 오브젝트를 생성하기 위한 방법 중 하나.

for 문과 if 문이 혼합된 단 한 줄의 코드로 iterable 한 객체를 생성할 수 있음

4 종류가 있으며, 예제는 아래와 같음

- List Comprehension

  - numbers = [n for n in range(10)]  # [0,1,2,3,4,5,6,7,8,9]

  - evens = [n for n in range(10) if n%2==0]  # [0, 2, 4, 6, 8]

- Set Comprehension 

  - evens = {n for n in range(10) if n%2==0}  # {0, 2, 4, 6, 8}

- Dict Comprehension 

  - example = {k:v for k,v in (('a',1),('b',2))}  # {'a': 1, 'b': 2}

- Generator Expression

  - 한 번에 모든 원소를 반환하지 않음. 한 번 실행 할 때 원소 하나만 반환

 

 

 

 

 

< if __name__ == "__main__":의 의미 >

eye.py 라는 이름의 파이썬 파일이 아래와 같이 존재하는 경우

< eye .py >

def echo(a):
    print(a)

if __name__ == "__main__":
    echo("Hello eyeballs!")

 

이 eye .py 를 직접 실행하면 eye .py의 __name__ 변수에는 "__main__" 값이 저장

따라서 if __name__=="__main__" 의 값이 True 이므로 echo("Hello eyeballs!") 를 실행함

 

만약 파이썬 쉘이나 다른 파이썬 모듈에서 eye 를 import 하는 경우

import eye

from eye import echo

eye.py의 __name__ 변수에 eye.py의 모듈 이름인 "eye" 가 저장

따라서 if __name__=="__main__" 의 값이 False 이므로 echo("Hello eyeballs!") 를 실행하지 않음

 

즉, if __name__=="__main__" 는 python 모듈을 직접 실행할 때

수행하는 코드를 넣는 부분임

 

 

 

< 특정한 위치에 존재하는 모듈 import 하기 >

import sys

print(sys.path)

위 명령어로 나오는 path 들은 python lib 가 설치되어있는 위치들임

이 sys.path 에 원하는 모듈이 존재하는 path 를 추가하면, 해당 모듈은 import 가 가능하게 됨

sys.path.append("/my/module/path")

 

 

 

 

 

 

 

 

 

< package 에 포함된 __init__.py 용도 >

1. 해당 dir 가 패키지의 일부임을 알려주는 역할

2. 해당 패키지 내 모듈에서 사용 가능한 공통 변수 및 함수를 넣을 수 있음

3. 해당 패키지 내 모듈에서 공통적으로 import 해야 할 다른 모듈을 import 해두어, 미리 import 할 수 있음

4. 해당 패키지 내 모듈에서 먼저 실행되어야 하는 공통 코드(이를테면 초기화 코드 등) 를 넣을 수 있음

 

 

 

 

 

< 일부러 에러 발생시키기 >

raise NotImplementedError

 

 

 

< GIL >

python global interpreter lock 에 대해 반드시 공부

 

 

 

 

 

 

참고

https://schoolofweb.net/blog/posts/category/%ed%94%84%eb%a1%9c%ea%b7%b8%eb%9e%98%eb%b0%8d/%ed%8c%8c%ec%9d%b4%ec%8d%ac/%ed%8c%8c%ec%9d%b4%ec%8d%ac-%ec%a4%91%ea%b8%89-%ea%b0%95%ec%a2%8c/

https://mingrammer.com/underscore-in-python/

https://mingrammer.com/understanding-the-asterisk-of-python/

https://wikidocs.net/91564#:~:text=print%20%ED%95%A8%EC%88%98%20%EB%8C%80%EC%8B%A0%20%EB%A1%9C%EA%B9%85%20%EB%AA%A8%EB%93%88,%EC%B6%9C%EB%A0%A5%EB%90%A8%EC%9D%84%20%ED%99%95%EC%9D%B8%ED%95%A0%20%EC%88%98%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4.

 

 

 

 

 

 

 

 

+ Recent posts