<application android:allowBackup="false" android:icon="@drawable/icon" android:label="@string/app_name" android:supportsRtl="true" android:usesCleartextTraffic="true" <!-- http:// 통신일 경우에도 통신 가능하도록 -->
Android 7.0(Nougat / API 24)에서 Intent로 URI 파일 경로 전송시 "file://" 노출되어 있으면 FileUriExposedException 오류가 발생하게 되고 앱이 종료된다. 앱간 파일을 공유하려면 "file://" 대신 "content://"로 URI를 보내야 한다. URI로 데이터를 보내기 위해선 임시 액세스 권한을 부여해야 하고 FileProvider를 이용해야 한다.
cacti 는 업계 표준 오픈 소스 데이터 로깅 도구인 RRDtool 프론트 엔드용으로 설계된, 오픈 소스 웹 기반 네트워크 모니터링 및 그래프 도구이다.
SNMP를 통해 네트워크 스위치 또는 라우터 인터페이스를 폴링하여 네트워크 트래픽을 감시한다. 각자의 그래프 세트를 가진 복수의 사용자를 처리 할 수 있어서 전용 서버, 가상 개인 서버 및 코로케이션을 공급하는 웹 호스팅 제공 업체가 고객들의 대역폭 통계를 확인하기 위해 사용 가능하다.
Cacti를 설치하기에 앞서 Cacti를 사용하려면 설치하려는 서버의 APM(Apache, php, MySQL)이 설정 되어 있어야 사용할 수 있다. https://link2me.tistory.com/1559 에 yum 으로 설치하는 APM에 대한 스크립트가 있는데, 이 기반위에 설치하는 스크립트를 테스트한 걸 기록해 둔다.
최신버전(Cacti Server v.1.2.8)으로 설치를 해서인지 삽질을 엄청 많이 하면서 스크립트를 수정 보완했다.
RRDTool 최신 버전 및 cacti 최신버전 설치 스크립트 과정이다.
Cacti는 RRD에 특화된 정교한 프론트 엔드 툴이다.
SSH Shell 프로그램에서 접속한 # root mode에서 작성한 것이다.
복사 붙여넣기를 편하기 하기 위해서 편의상 #은 삭제했다. 간혹 #이 붙은 것은 주석처리 표시이다.
rpm -e --nodeps gmp.x86_64 yum -y erase rh-php70-php-gmp 즉 상황에 맞게 구글링해서 처리해야 하는데, 정보 검색이 잘못될 수 있고, 그러면 고생을 좀 할 수 있다.
# net-snmp 패키지를 설치하였다면 /etc/snmp/snmpd.conf 파일에서 4가지 설정사항을 수정해줘야 한다. vi /etc/snmp/snmpd.conf com2sec public default public group public v1 public group public v2c public view all included .1 access public "" any noauth exact all none none
/etc/rc.d/init.d/snmpd stop /etc/rc.d/init.d/snmpd start # 또는 service snmpd restart
### RRDTOOL 설치 #### RRD툴(RRDtool, round-robin database tool)은 시간에 따른 자료를 다룰 수 있는 도구이다. 그림 형식으로 시각화 해주는 기능도 있다. MRTG를 모방해 일반화 하여 개발한 도구이며 자료 수집에 관한 일부분이 빠져 완전한 대체물은 아니지만, 네트워크 모니터링 분야에서 매우 인기있는 도구이다. 펄, 파이썬, 루비, Tcl, PHP를 지원한다. 라운드 로빈 데이터베이스(Round Robin Database, RRD)는 Tobias Oetiker에 의해 제작되었으며 현재는 전 세계의 수많은 사람들의 참여로 진행되고 있는 오픈 소스 기반의 소프트웨어이다. Round Robin은 고정된 크기의 데이터와 현재 element에 대한 포인터로 동작하는 기술로 현재 데이터를 읽고 쓸 때 포인터는 다음 element로 이동하게 된다. 시작과 끝이 없는 원과 같이, Round Robin 기술을 사용하면 계속해서 데이터를 읽고 쓰는 작업이 가능하다. 사용하는 중에도 모든 가용 위치에 대한 사용이 가능하며, 자동적으로 이전의 위치에 대한 재사용이 가능하다. 이러한 방법으로 데이터베이스는 크기는 증가하지 않지만 어떠한 인위적인 작업 없이 사용 가능하게 된다.
#### RRDtool 설치: source 설치 방법
## 배포 사이트 : https://oss.oetiker.ch/rrdtool/ 에서 최신버전을 확인한다. ## cacti 에서 권장하는 버전을 설치하는 것이 좋을 것이다. cd /root/rrdtool wget https://oss.oetiker.ch/rrdtool/pub/rrdtool-1.4.9.tar.gz tar xvfz rrdtool-1.4.9.tar.gz cd rrdtool-1.4.9 ./configure --prefix=/usr/local/rrdtool --disable-python --disable-tcl make && make install echo $? 여기서는 컴파일 작업 중 아무런 문제가 발생되지 않아 0 이 나타났다. 만약 0 이 아닌 다른 숫자가 나오면 제대로 설치가 안되었다는 것이더라.
### uninstall 하는 방법 # 처음 설치한 /usr/local/rrdtool 폴더를 찾아서 지운다. cd /usr/local/ rm -rf rrdtool/ # 소스를 설치한 폴더로 이동하여 make uninstall 한다. cd /root/rrdtool/rrdtool-1.7.2 ./configure make uninstall
#### cacti 설치하기 : cacti source 설치 방법 ## https://www.cacti.net/download_cacti.php 에서 최신 버전을 확인한다. # The latest stable version is 1.2.8, released 12/08/19. # Cacti requires MySQL, PHP, RRDTool, net-snmp, and a webserver that supports PHP such as Apache. # Requirements : RRDTool 1.4+ recommended, MySQL 5.x or greater, PHP 5.1 or greater
cd /usr/local/src wget http://www.cacti.net/downloads/cacti-1.2.8.tar.gz tar zxvf cacti-1.2.8.tar.gz # mv cacti-1.2.8 cacti cp -Rf cacti-1.2.8 /var/www/html/cacti
# cacti 디렉토리 권한 설정
cd /var/www/html/cacti #chown cacti.cacti /var/www/html/cacti -R chmod 755 /var/www/html/cacti
# 데이터 파일과 로그 파일이 저장되는 디렉토리 등 퍼미션을 777로 설정한다. cd /var/www/html/cacti chmod 777 rra log scripts chmod 777 -R cache/ chmod 777 -R resource/ chmod 777 /var/lib/php/session
## cacti DB 생성 및 사용자 권한 등록 cd /var/www/html/cacti mysql -u root -p 비밀번호 입력
show databases; drop database cacti; -- db delete create database cacti default character set utf8mb4 COLLATE utf8mb4_unicode_ci; grant all on cacti.* to cactiuser@localhost identified by 'cactiuser'; -- 패스워드 반드시 변경한 걸 적어주시라. GRANT ALL PRIVILEGES ON mysql.* TO cactiuser@localhost WITH GRANT OPTION; flush privileges;
-- cacti 테이블 생성 show databases; use cacti; source cacti.sql -- 육안으로 확인한다. 에러가 발생한 부분이 어떤 것인지 등등... quit
==================================================== ## cacti 테이블 생성 cd /usr/local/src/cacti mysql cacti < ./cacti.sql -uroot -p # 에러가 발생하면 이후 테이블 생성이 제대로 안되는 증상 발생하더라. ====================================================
# httpd.conf 찾기 find / -name httpd.conf vi /etc/httpd/conf/httpd.conf
## php.ini 파일 찾기 find / -name php.ini vi /etc/php.ini memory_limit = 800M max_execution_time = 60 date.timezone = Asia/Seoul 수정하고 저장한다.
#MariaDB 설정 ## vi /etc/my.cnf 을 아래와 같이 수정한다. vi /etc/my.cnf
# cacti web 설정시 문제가 되면 SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_%' 로 확인하여 수정해야 한다. # https://jsonobject.tistory.com/408 에 나온 내용을 참조하면 도움된다.
# MySQL 구동 service mysql start service mysql stop service mysql restart
#서비스 구동 service httpd start service httpd stop service httpd restart
## Crontab poller.php 등록 # vi /etc/cron.d/cacti 을 아래와 같이 설정한다 vi /etc/cron.d/cacti */5 * * * * cacti /usr/bin/php /var/www/html/cacti/poller.php > /dev/null 2>&1
### Web 으로 설치 ### # http://서버IP/ cacti 로 접속하여 install버튼을 이용하여 설치를 마무리 한다.
## 기본 접속 세팅
RRDTool Binary Path :/usr/local/rrdtool/bin/rrdtool 로 수정해서 적어준다.
## User Login admin / admin
## 패스워드 변경하라고 하면 패스워드를 변경한다.
# 반드시 대문자, 숫자, 특수문자를 포함해서 8자리 이상으로 변경해야 한다.
원하는 설정을 100% 충족해야만 다음으로 넘어간다.
service httpd restart 를 해주면 Web - No 가 사라진다.
## cacti 그래프 확인
# 서버에 가서 아래와 같은 명령어를 입력하면 그래프가 생성된다.
# whereis php 로 php 경로를 찾아 맞게 수정한다. /usr/bin/php /var/www/html/cacti/poller.php –force
객체 지향 언어에서는 객체를 생성하기 위해서는 먼저 클래스를 선언하고, 해당 클래스로부터 객체를 생성한다.
하지만, 코틀린에서는 이런 과정을 거치지 않고도 객체를 생성할 수 있다.
object 키워드를 사용하면, 클래스가 아니라 객체 그 자체, 인스턴스가 된다.
코틀린에는 static keyword가 없고 object 키워드를 통해 - 싱글턴을 정의하는 방법 - 동반객체 companion object를 이용한 팩토리 메서드 구현 - 무명클래스(익명 클래스)의 선언 를 한다.
예제1
fun main(){ // Person은 그 자체로 하나의 객체이기 때문에, 바로 .을 찍어 프로퍼티나 메서드에 접근할 수 있다. Person.name = "홍길동" Person.age = 30 println("이름 : ${Person.name}, 나이 : ${Person.age}") }
// object 는 subclass 의 명시적인 선언 없이 객체 생성 object Person { var name: String = "" var age: Int = 0 }
예제2
- object 키워드는 어떤 객체가 반드시 하나만 존재해야 할 때, 즉 싱글톤 패턴에 활용할 수 있다.
// 예제 2.1
fun main(args: Array<String>) { Singleton.printVarName()
println("\nSingleton variableName Change") Singleton.variableName = "강감찬" var a = A() }
object Singleton{ init { println("Singleton class invoked.") } var variableName = "홍길동" fun printVarName(){ println("변수명 : $variableName ") } }
class A { init { println("Class init method. Singleton variableName property : ${Singleton.variableName}") Singleton.printVarName() } }
// 예제 2.2 fun main(args: Array<String>) { var a = A() a.printVarName()
println() Singleton.printVarName() }
open class A { open fun printVarName() { println("I am in class printVarName") }
class CarSales(val name: String){ // 코틀린에는 정적(static) 변수 혹은 메소드가 없고, // 대신 패키지 내에 함수를 선언하여 사용할 수 있다. companion object { var total = 0 }
var count = 0 fun sales(){ total++ count++ } }
예제4
fun main(args: Array<String>){ var first = MyHtml.generateHtmlObject("first") first.setColumnHeaders("1열","2열","3열") //println("first.generateTable() => ${first.generateTable()}")
val second = MyHtml.generateHtmlObject("second", 5,5) second.setColumnHeaders("월","화","수","목","금")
val third = MyHtml.generateHtmlObject("third",2,7) third.setColumnHeaders("적색","녹색","노랑","파랑","보라","검정","주황")
object MyRecord { private var _count = 0 private val _tables = hashMapOf<String,MyHtml>()
fun gen(table: MyHtml){ _tables.put(table.name, table) _count++ } fun showTableCount() = _count fun showTable(): Map<String,MyHtml> = _tables }
class MyHtml { val name: String val row: Int val col: Int private val HEAD = "<table border='1' cell-spacing='10' cell-padding='10'" private val TAIL = "</table>" private lateinit var comment: String private val tableColumns = ArrayList<String>()
private constructor(_name: String, row: Int, col: Int ){ this.name = _name this.row = row this.col = col }
fun setColumnHeaders(vararg colNames: String){ this.tableColumns.addAll(colNames) }
companion object _GenTable { fun generateHtmlObject(_name: String, row: Int = 2, col: Int = 3): MyHtml { val mh = MyHtml(_name, row, col) mh.comment = "<!-- auto-generator table by MyHtml v1.0 -->" // 테이블 객체 기록 MyRecord.gen(mh) return mh } }
private inner class Tr { val rowHtml = """ |<tr> | ${"<td>-</td>".repeat(col)} |</tr>${"\n"} """.trimMargin() }
private inner class Th(val wpx: Int = 40) { val headers = tableColumns.map { "<th width='$wpx'> $it </th>" } val rowHtml = """ |<tr> | ${headers.joinToString("")} |</tr> """.trimMargin() }
// 테이블 생성 실행 fun generateTable() = """ |$comment |$HEAD |${Th().rowHtml} |${Tr().rowHtml.repeat(row)} |$TAIL """.trimMargin() }
코틀린에서는 접근자와 설정자를 따로 만들어줄 필요가 없다. 자바에서는 private 접근제한자로 은닉화된 멤버 변수에 getter/setter 메소드를 사용해서 접근한다. 코틀린은 property가 getter/setter를 대체한다.
코틀린에서 속성(property)란 최상위 변수(함수나 클래스 외부에 정의된 변수)나 클래스의 멤버 변수로 선언하면 속성으로 간주된다. 클래스의 멤버 변수는 모두 private 제한자로 지정된다. 따라서 해당 클래스의 내부의 getter와 setter를 통해서만 속성을 참조할 수 있다.
package com.myapp.funcargs
fun main(){ val a = Person("홍길동", 30) println(a) //println(a.toString())
val b = a.copy("이순신") println(b)
val c = b.copy(age=40) println(c)
val(name, age) = Person("강감찬",35) println("성명 : $name, 나이 : $age")
val obj = Pair<String, Int>("김좌진", 50) println(obj) }
// 데이터는 보유하지만 아무것도 안하는 클래스 data class Person(val name: String, val age: Int) // 파라미터 없는 기본 생성자가 필요한 경우에는 // data class Person(val name: String = "", val age: Int = 0) // 모든 프로퍼티에 기본 값을 설정해 주면 된다. // 기본 생성자에서 선언된 속성을 통해, 컴파일러가 자동으로 toString(), hashCode(), equals() 등을 생성한다. // 명시적으로 정의해준 경우에는 컴파일러가 자동으로 생성하지 않는다.
실행결과
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class Memo_AddEdit (
var idx: String,
var memo: String,
var userID: String,
var userNM: String,
): Parcelable
class MainActivity : AppCompatActivity() {
private lateinit var mContext: Context
private lateinit var mMemo_AddEdit: Memo_AddEdit
override fun onCreate(savedInstanceState: Bundle?) {
맥OS 10.15 카탈리나는 100% 64bit 기반 운영체제다. 애플은 이전 버전인 맥OS ‘모하비(Mojave)’가 32bit를 지원하는 마지막 맥OS가 될 것이며, 카탈리나부터는 더이상 32bit 앱을 사용할 수 없다고 강조해왔다. 지난 4월 맥OS 업데이트에서는 32bit 앱 실행 시 업데이트 경고 창을 띄우는 기능을 추가하면서 32bit 지원 종료를 꾸준히 예고해왔다.
하지만 애플의 경고에도 불구하고 적지 않은 사용자들이 구형 프로그램을 계속 사용하고 있으며, 그로 인해 이번 카탈리나 업데이트 이후 앱 실행 불가 문제가 더욱 크게 불거지고 있다고 더 버지 등 외신들은 지적했다.
실행 불가 문제는 어도비의 ‘포토샵(Photoshop)’이나 ‘라이트룸(Lightroom)’같이 주로 사진과 영상 편집, 음악 작곡 및 편집 등 고가의 전문가용 애플리케이션에서 주로 발생하는 것으로 나타났다. 업그레이드 비용이 너무 비싸거나, 정기적으로 사용료를 내는 구독형 서비스가 부담스러운 사용자들이 추가 과금 없이 영구적으로 사용할 수 있는 구버전 애플리케이션을 사용해 왔는데, 카탈리나 업데이트로 피해를 보고 있다는 것.
고차함수(High-order Function)란 다른 함수를 인자로 사용하거나, 함수를 결과값으로 반환하는 함수를 말한다.
package com.myapp.funcargs
fun main(){ val a = sum(4,5) // 일반 인자 println("a result : $a") val b = mul(sum(1,2), 3) // 인자에 함수 사용 println("b result : $b")
println("ResultFun : ${resultFun()}") }
fun sum(a: Int, b: Int) = a + b fun mul(a: Int, b: Int) = a * b fun resultFun(): Int { return mul(3,5) // 함수의 반환 값으로 함수 사용 }
람다 표현식
람다식, 또는 람다 함수는 프로그래밍 언어에서 사용되는 개념으로 익명 함수 (Anonymous Functions)를 지칭하는 용어이다.
val lambdaName: Type = {argument -> codeBody}
람다 함수는 { } 안에 매개변수와 함수 내용을 선언하는 함수로 다음 규칙에 따라 정의한다. - 람다 함수는 항상 { }으로 감싸서 표현해야 한다. - { } 안에 -> 표시가 있으며 -> 왼쪽은 매개변수, 오른쪽은 함수 내용이다. - 매개변수 타입을 선언해야 하며, 추론할 수 있을 때는 생략할 수 있다. - 함수의 반환값은 함수 내용의 마지막 표현식이다.
fun sum(a: Int, b: Int) = a + b 를 람다 함수로 정의하면 val sum ={a: Int, b: Int -> a + b}
또는
val sum: (Int, Int) -> Int ={a, b -> a + b}
package com.myapp.funcargs
fun main(){ val a = sum(4,5) // 일반 인자 println("a result : $a") val b = mul(sum(1,2), 3) // 인자에 함수 사용 println("b result : $b")
println("ResultFun : ${resultFun()}")
val c:(String)->Unit ={str -> println("$str 람다함수")} b(c) }
val sum ={a: Int, b: Int -> a + b} // val sum: (Int, Int) -> Int ={a, b -> a + b} // val mul ={a: Int, b: Int -> a * b} val mul: (Int, Int) -> Int ={a, b -> a * b} fun resultFun(): Int { return mul(3,5) // 함수의 반환 값으로 함수 사용 } fun b (function: (String)->Unit){ function("b가 호출한") }
확장함수(Extension Function) Extension functions(확장 함수)는 기존에 정의된 클래스에 함수를 추가하는 기능이다. 자신이 만든 클래스는 새로운 함수가 필요할 때 쉽게 추가할 수 있다. 하지만 Standard Library 또는 다른 사람이 만든 라이브러리를 사용할 때 함수를 추가하기가 매우 어렵다.
확장 함수는 fun 클래스이름.함수이름(매개변수): 리턴타입 { 구현부 }으로 정의할 수 있다.
또는 fun 확장하려는대상.함수명(매개변수):Unit { }
fun 확장하려는대상.함수명(매개변수):리턴타입 { return 값 }
확장함수 예제1
class Car { fun getPrice() : Int { return 8000 } }
fun Car.getBrandName() : String { return "BMW" }
fun Car.getBrandName2() : Car { println("KIA") return this }
fun main(args: Array<String>) { val car = Car() println(car.getBrandName()) println(car.getBrandName2().getPrice()) }
확장함수 예제2
fun String.getLonggerLength(x: Int) : Int { return if(this.length > x) this.length else x }
fun main(args: Array<String>) { println("Hello".getLonggerLength(3)) }
fun main(args: Array<String>) { println("Hello".getLonggerStringLength("Have a nice day!")) }
fun String.getLonggerStringLength(x: String) : Int { return if(this.length > x.length) this.length else x.length }
확장함수 예제3
class Car { fun getPrice(): Int { return 10000 } }
fun Car.getPrice(): Int { return 20000 }
fun Car.getPrice(price: Int): Int { return 30000 }
fun main(args: Array<String>) { val car = Car() println(car.getPrice()) println(car.getPrice(0)) }
확장함수 예제4
fun List<Int>.getHigherThan(num: Int): List<Int> { val result = arrayListOf<Int>() for (item in this) { if (item > num) { result.add(item) } } return result }
fun main() { val numbers: List<Int> = listOf(1, 2, 3, 4, 5, 6, 7) val filtered = numbers.getHigherThan(3).toString() System.out.println(filtered) }
자바에서는 접근 제한자(Access Modifier)라는 용어를 사용하고 코틀린에서는 Visibility Modifier로 사용하는데, 우리말로는 접근 제한자 또는 가시성 제한자라고 할 수 있다. 코틀린에서 접근 제한자를 가질 수 있는 요소로는 class, object, interface, constructor, function, property 등이 있다.
자바에서는 접근 제한자를 붙이지 않으면 default(package-private) 이다. 그러나 코틀린에서는 아무것도 붙이지 않으면 public 이다.
코틀린에서 말하는 모듈은 자바와는 약간의 차이가 있는데, 자바에서의 모듈은 기능이 비슷한 클래스들의 집합체를, 코틀린에서는 동일한 컴파일의 집단을 의미한다.
패키지 레벨의 접근제한자
프로퍼티와 함수를 최상위(Top) 레벨에 작성한다는 것은 클래스에 멤버로 포함하지 않고 코틀린 파일에 정의하는 것을 의미한다.
package foo
public val myData: Int = 10 public fun myFun() {} public class myClass() {}
- 접근 제한자를 표기하지 않으면 public이 기본으로 설정되며, 누구나 접근이 가능하다.
- private : 해당 .kt 파일 내부에서만 사용 가능하다. - internal : 프로젝트의 모듈 안에서 누구나 접근이 가능하다. - protected : 최상위(top-level)에서는 사용할 수 없다.
클래스와 인터페이스의 접근제한자 클래스 또는 인터페이스 안에 선언되는 멤버들에 사용되는 접근 제한자는 다음과 같은 의미를 갖는다. - public : 누구나 접근이 가능하다. - private : 클래스 내부에서만 접근이 가능하다. - protected : 클래스 내부와 상속받는 객체에서 접근이 가능하다. - internal : 프로젝트의 모듈 안의 누구나 접근이 가능하다.
private, protected, public 과 Java와 코틀린에서 같은 의미로 사용된다. 하지만 internal은 자바에서는 default(같은 패키지내에서만 접근가능) 코틀린에서는 같은 모듈안에서만 사용 가능하다.
생성자의 접근제한자 생성자에도 접근제한자를 붙일 수 있다. 기본은 public 생성자로 누구나 접근이 가능하다. internal을 붙이면 모듈 내에서만 접근 가능하다. private을 붙이면 클래스 내에서만 접근이 가능하여 외부에서 생성할 수 없다.
// private 생성자 class Person private constructor(a: Int) { }
Java 접근 제한자
접근 제한자는 Class 의 멤버에 대해 다른 Class 에서 접근하는 것을 제한하는 용도로 사용된다.
- public : 모든 Class 에서 접근 가능
- protected : 같은 패키지에 있는 Class, 그리고 다른 패키지의 자손 클래스에서 접근이 가능
- default : 같은 패키지에 있는 Class 들만 접근 가능
String name; // 아무런 접근 제한자도 지정되지 않았으므로 default 접근 제한자다.
코틀린에서 프로젝트(Project)는 모듈(Module), 패키지(Package), 파일(File)로 구성되어 있다. 프로젝트에는 모듈이 있고 모듈은 다시 패키지로 구성되어 있다.
Java는 파일명과 클래스명이 동일해야 하지만, 코틀린은 public 클래스는 하나만 사용 해야하는 규칙이 없다.
코틀린 파일은 .kt 확장자를 가지며 맨 위에는 이 파일이 어떤 패키지에 포함된 것인지 코틀린 컴파일러가 알 수 있도록 패키지 이름을 선언해야 한다. 만약 패키지 이름을 선언하지 않으면 그 파일은 자동으로 default 패키지에 포함된다. 파일이 패키지 안에 들어있어도 패키지 이름을 선언하지 않으면 default 패키지에 포함된 것으로 인식한다. 파일에 클래스가 여러 개 정의되어 있다면 파일은 단순히 클래스를 묶는 역할을 하고 kt 확장자가 붙게 된다. 코틀린에서는 파일명과 클래스의 선언 개수에 큰 의미를 두지는 않는다. 같은 파일에 있는 여러 개의 클래스는 모두 그 파일에서 지정한 패키지로 인식한다.
패키지 이름은 파일 맨 위에 적는다. 이때 패키지 이름 앞에 package라는 키워드를 함께 입력해야 패키지 이름으로 인식한다. 단, 패키지의 이름은 특수 문자나 숫자로 시작하면 안된다.
abstract class Person(name: String) { init { println("My name is $name.") } // 일반 메서드 fun displaySSN(ssn: Int) { println("My SSN is $ssn.") } // 추상 메서드 abstract fun displayJob(description: String) }
class Teacher(name: String): Person(name) { // 추상 메서드 구현 override fun displayJob(description: String) { println(description) } }
fun main(args: Array<String>) { val jack = Teacher("Jack Smith") jack.displayJob("I'm a mathematics teacher.") jack.displaySSN(23123) }
코틀린은 자바, C++과는 달리 클래스 선언 부분에서 이름에 괄호를 붙여 생성자를 만들 수 있다.
자바는 멤버변수를 field라고 하지만 코틀린 프로퍼티라는 용어를 사용한다. 자바는 생성자를 꼭 정의해야 하지만 코틀린은 생략이 가능하다.
코틀린에는 주 생성자와(primary constructor)와 부생성자가(secondary constructor)가 존재한다. 주 생성자는 class 선언과 함께 선언하고, 부 생성자는 추가적인 생성자가 필요할 때 사용한다. 생성자를 정의하지 않으면 기본으로 인자가 없는 생성자를 만들어 준다.
부모의 생성자는 super(), 클래스 내 생성자중 다른 생성자는 this()로 호출이 가능하다.
fun main() { var a = Person("박보영", 1990) //코틀린은 new 키워드 없이 인스턴스를 생성한다. var b = Person("전영경",1997) var c = Person("차은우") var d = Person("홍길동")
// 클래스 별로 여러 개를 가질 수 있다. constructor(name: String) : this(name,1997) }
기본 생성자
- 기본 생성자에 어노테이션 접근지정자 등이 있을 경우 constructor 키워드가 필요하다.
class Person public @Inject constructor(name: String) { .... }
클래스 상속
- 코틀린의 최상위 클래스는 Any 이다.
Any는 java.lang.Object 와는 다른 클래스이며, equals(), hasCode(), toString()만 있다.
- 상속 키워드는 콜론(:)이다. Java 의 상속 키워드는 extends
- 코틀린의 클래스와 메소드는 기본적으로 final이다. 즉 기본적으로 상속을 못하게끔 되어 있다.
Java에서는 클래스나 메서드에 final 키워드를 붙여 클래스를 더 이상 상속받지 못하게 하거나,
메서드를 재정의하지 못하게 할 수 있다. - 클래스의 상속을 허용하려면 클래스 앞에 open 변경자를 붙여야 한다.
open 변경자가 없으면 final 로 자식 클래스에서 override가 불가능하다.
오버라이드를 허용하고 싶은 메소드나 프로퍼티 앞에도 open 변경자를 붙여야 한다.
- 부모 클래스의 메소드를 자식 클래스에서 오버라이드 한 경우, 해당 메소드는 기본적으로 열려있다.
fun main() { var a = Animal("불독", 3,"개") var b = Dog("불독", 5)
a.introduce() b.introduce() b.bark()
var c = Cat("나갈래", 4) c.introduce() c.meow() }
open class Animal(var name:String, var age:Int, var type:String){ open fun introduce(){ // open 이므로 override 가능하다. println("저는 ${type} : ${name}, ${age} 살 입니다.") } }
class Dog(name:String, age:Int) : Animal (name,age,"개"){ // 상위 클래스 함수에 open 키워드를 명시하고, 서브 클래스 함수에 override 를 붙인다. override fun introduce(){ println("타입 : 개, ${name}, ${age} 살 입니다.") }
fun bark(){ println("멍멍멍") } }
class Cat(name: String, age: Int) : Animal(name, age, "고양이"){ fun meow() { println("야옹야옹") } }
fun foo2() { var ints = listOf(0, 1, 2, 3) // 람다식에서 return 시 nearest enclosing 함수가 return 된다. ints.forEach { if(it == 1) return print(it) } print("End") }
fun foo3() { var ints = listOf(0, 1, 2, 3) // 람다식에 대해서만 return 하려면 label 을 이용해야 한다. ints.forEach loop@{ if(it == 1) return@loop print(it) } print("End") }
fun foo4() { var ints = listOf(0, 1, 2, 3) // 람다식에 대해서만 return 하려면 label 을 이용해야 한다. // 암시적 레이블은 람다가 사용된 함수의 이름과 동일하다. ints.forEach { if(it == 1) return@forEach print(it) } print("End") }
fun foo5(): List<String> { var ints = listOf(0, 1, 2, 3) val result = ints.map{ if(it == 0){ return@map "zero" } "No $it" } return result }
맥북PRO 2015 16G 를 구입하여 SSD 만 1TB로 교체하여 사용하려고 검색한 결과를 적어둔다.
애플이 2013년부터 SATA 기반이 아닌 PCI 익스프레스 기반의 SSD를 맥북에 탑재하면서 저장장치 성능은 빨라졌지만 기판과 커넥터가 애플 독자 규격으로 설계되면서 맥북을 구매할 때 용량이 높은 SSD를 선택하지 않는 한 업그레이드가 어려웠다.
애플은 2015 맥북 제품부터 자체 설계한 'AP' 컨트롤러를 탑재한 PCIe x4 레인 방식의 SSD를 사용하고 있다. 덕분에 2015 제품의 순차 읽기∙쓰기 속도가 크게 개선된 바 있다.
정밀 청소와 배터리 교체만 해줘도 맥북 오래 사용할 수 있다. 2015 맥북프로 15인치 배터리 용량은 8755mh 이다. 15형 맥북 프로 Mid 2015 레티나 모델로 2015년 9월에서 2017년 2월 사이에 주로 판매된 제품에서 문제가 있어리콜을 해주고 있다.
15년 맥북프로 15인치 공식 배터리 교체비 : 249,000원 애플은 내장 키보드 있는 상판을 통으로 교체한다. 정확한 금액은 아래에서 가까운 지역 애플 서비스센타로 방문 문의한다.
→ 상판교체를 한 것인지에 대한 정보를 확인한다.
SSD 교체 검토
맥북프로 레티나 기종은 SSD 저장장치 이외에 업그레이드 불가능하다. 2017 터치바 모델은 SSD 저장장치 교체도 불가능하다. 판매되는 2.5인치 SSD가 아닌 스틱형의 SSD를 사용하여 가격이 상당히 비싸다.
트랜센드 JetDrive 820 960GB 가격은 61만원 정도 된다.
트랜센드 JetDrive 850 960GB 가격은 70만원 약간 넘는다. (2020년 1월 2일 옥션 사이트 기준)
사용후기를 찾아보면 아마존에서 - Samsung 970 EVO 1TB - NVMe PCIe M.2 2280 SSD (Plus 를 사면 절대 안됨) - Sintech NGFF M.2 nVME SSD Adapter Card for Upgrade 2013-2015 Year Macs 를 구입하여 직접 증설한다. 증설방법은 유투브에 동영상으로 나온다. 증설시 배터리 전원선을 반드시 뽑고 진행해야 한다.