๊ฐœ๋ฐœ/Android

[Android] 100% ์ดˆ๋ณด์ž๋ฅผ ์œ„ํ•œ RxJava(RxJava for 100% beginners)-part 1

๋„๋ฆฌ ๐ŸŸ 2021. 4. 8. 23:06

์ž‘์„ฑ์ผ: 2018.03.07


์ด ๊ธ€์€ ์›๋ฌธ์„ ๋ฒˆ์—ญํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

RxJava for 100% beginners(part 1)

์š”์ฆˆ์Œ ๋งŽ์€ Android ๊ฐœ๋ฐœ์ž๋“ค์ด “Reactive Programming/RxJava”์˜ ์กด์žฌ๋ฅผ ์ž˜ ์•Œ๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ตœ๊ทผ Android Weekly์—๋Š” ๋งค ์ฃผ RxJava์™€ ๊ด€๋ จ๋œ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๊ฐ€ ํ•ญ์ƒ ์žˆ๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค. 

ํ•˜์ง€๋งŒ, RxJava์˜ ๋ฌธ๋ฒ•์— ์‚ฌ์šฉ๋œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๋Œ€ํ•ด ์ž˜ ๋ชจ๋ฅด๋Š” ์ดˆ๋ณด์ž์—๊ฒŒ๋Š” ์กฐ๊ธˆ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Scala์—์„œ๋Š”, RxJava์˜ map(), filter(), flatmap() ๋“ฑ๊ณผ ์œ ์‚ฌํ•œ ์˜คํผ๋ ˆ์ดํ„ฐ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ด์ „์— Scalar๋‚˜ (RxJava๊ฐ€ ๊ฐœ๋…๊ณผ ๋ฌธ๋ฒ•์„ ์ฐจ์šฉํ•œ) ๋‹ค๋ฅธ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•ด๋ดค๋‹ค๋ฉด, ์•„์˜ˆ ์จ๋ณด์ง€ ์•Š์€ ์‚ฌ๋žŒ๋ณด๋‹ค๋Š” ๋‚˜์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์‹œ๋ฆฌ์ฆˆ์—์„œ๋Š”, ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์•„์˜ˆ ๋ชจ๋ฅด๋Š” ๋ถ„๋“ค์„ ์œ„ํ•ด ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ธฐ๋ณธ๋ถ€ํ„ฐ ๋‹ค๋ฃจ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋‹คํ–‰ํžˆ Java8๋ถ€ํ„ฐ Stream API๊ฐ€ ์ถ”๊ฐ€๋๊ณ , “Stream” ํด๋ž˜์Šค๋Š” RxJava์˜ “Observable”๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ RxJava์˜ Observable์„ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด Java8์˜ Stream API์˜ ๋ช‡๋ช‡ ์š”์†Œ๋ฅผ ํ•จ๊ป˜ ์‚ดํŽด๋ณผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. 

๊ทธ๋Ÿผ ์ด์ œ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค! 

์ดˆ๋ณด์ž๋ถ„๋“ค์„ ์œ„ํ•ด, ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ 2๊ฐ€์ง€ ์ฃผ์š” ์ด์ ๋ถ€ํ„ฐ ๋ณด๋ฉด: 

  1. ํ•จ์ˆ˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ณ , ์ด๊ฒƒ์„ “first-class object”๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
  2. ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ operation์„ ์ˆ˜ํ•™์—์„œ์˜ “ํ•จ์ˆ˜”์ฒ˜๋Ÿผ ๋‹ค๋ฃน๋‹ˆ๋‹ค. -> ์šฐ๋ฆฌ๋Š” ๋งˆ์ง€๋ง‰ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋‚˜ ๋ฐ˜ํ™˜๊ฐ’์—๋งŒ ์‹ ๊ฒฝ์“ฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค. (์ƒํƒœ ๋ณ€ํ™”๋‚˜ ๊ฐ์ฒด์˜ ๋ณ€๊ฒฝ์„ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.)


RxJava์™€ Java8 Stream API๋Š” ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋žŒ๋‹ค ํ‘œํ˜„์‹์€ ์œ„์˜ ์ฒซ๋ฒˆ์งธ ์žฅ์ ์„ ์ž˜ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค: 

interface NewAction{
		void call();
}

public static void execute(NewAction action){
    action.call();
}
public static void main(String[] args) {

    execute( new NewAction() {

        @Override
        public void call() {
            // TODO Auto-generated method stub
            System.out.println("action start");
        }
    } );


    execute(()->System.out.println("action start"));
}

line 11 execute( new NewAction(... ๊ณผ line 21 execute(()->... ์˜ ์ฐจ์ด์ ์„ ๋ณด๋ฉด, ๋‘˜ ๋‹ค “action start”๋ฅผ ์ถœ๋ ฅํ•˜์ง€๋งŒ, line 21์ด ํ›จ์”ฌ ์งง์Šต๋‹ˆ๋‹ค. ์ด์ œ “NewAction” ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  “call” ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ ๋ฐ”๋””์— ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  execute()๋กœ ์ „๋‹ฌ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. That’s not magic! 

Java 8์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ๋งˆ์Œ๊ป ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ๋งŒ ํฌํ•จํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์ฝ”๋“œ์—์„œ ์ธํ„ฐํŽ˜์ด์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ , ๋ฉ”์„œ๋“œ ๋ฐ”๋””๋ฅผ ์›ํ•˜๋Š” ๊ณณ์— ์ž‘์„ฑํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.


๊ทธ๋Ÿฌ๋‚˜, ์—ฌ๊ธฐ์„œ ํ•จ์ˆ˜๊ฐ€ ์ •๋ง ํŒŒ๋ผ๋ฏธํ„ฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉ๋˜๋Š” ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. ๋‹จ์ง€ ์ ์€ ์ฝ”๋“œ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ฃผ๊ณ , ํ•จ์ˆ˜๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ์ฒ˜๋Ÿผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•˜๋Š” ์ปดํŒŒ์ผ๋Ÿฌ์˜ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค. 

์ž, ๊ทธ๋Ÿผ 2๋ฒˆ ์žฅ์ ์˜ ์˜ˆ์‹œ๋ฅผ ์•„๋ž˜์—์„œ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

public static void main(String[] args) {
		ArrayList<Integer> arrayList = new ArrayList<Integer>();
		arrayList.stream()
		         .map(integer -> integer + 1)
		         .filter(integer -> integer < 10)
		         .limit(10);
}

์œ„ ์˜ˆ์ œ์—์„œ, ArrayList๋ฅผ Stream(Java์˜ InputStream์ด๋‚˜ OutStream ๊ฐ™์€ ๊ฐœ๋…์ด ์•„๋‹™๋‹ˆ๋‹ค)์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , ๋‹ค๋ฅธ ์—ฐ์‚ฐ์ž์— ์ ์šฉํ•˜์—ฌ Stream์„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋จผ์ € Stream(Integer ๊ฐ์ฒด์˜ ์ŠคํŠธ๋ฆผ)์˜ ๋ชจ๋“  ์š”์†Œ์— 1์”ฉ ๋”ํ•˜๋„๋ก map์„ ์ ์šฉํ•˜๊ณ , 10๋ณด๋‹ค ์ž‘์€ ์ •์ˆ˜๋กœ ํ•„ํ„ฐ๋งํ•œ ํ›„, ์•ž์—์„œ 10๊ฐœ์˜ ์š”์†Œ๋งŒ ์ŠคํŠธ๋ฆผ์—์„œ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค. ์—ฐ์‚ฐ์ž๋ฅผ ์ ์šฉํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์ด “์ƒ์„ฑ”๋˜๋ฉฐ, ๊ทธ๋ž˜์„œ ์—ฐ์‚ฐ์ž๋“ค์„ ์—ฐ์‡„๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

๊ทธ๋Ÿฐ๋ฐ ์ž ๊น.. ์ด๋Ÿฐ ํŠน์ดํ•œ ๊ตฌ์กฐ๋ฅผ ์จ์„œ ์ข‹์€ ์ ์€ ๋ญ˜๊นŒ์š”? 

 

์•ž์—์„œ ์ด๋ฏธ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์— ๋Œ€ํ•ด ์–ธ๊ธ‰์„ ํ–ˆ์—ˆ๋Š”๋ฐ, “์ƒํƒœ”๋ฅผ ๊ฐ–์ง€ ์•Š๊ณ  ํ•จ์ˆ˜ ์‹คํ–‰์˜ ๊ฒฐ๊ณผ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ž…๋‹ˆ๋‹ค. 

์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜์™€ ๊ฐ™์€ ํ•จ์ˆ˜ ์‹คํ–‰ ์ˆœ์„œ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด:

funcA() -> funB() -> funcC();

์ด ๊ฒฝ์šฐ, funC()๋Š” funcB()์˜ ๊ฒฐ๊ณผ๋งŒ ๋‹ค๋ฃจ๊ณ , funcA()์˜ ๊ฒฐ๊ณผ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๊ฐ€์žฅ ์ด์ƒ์ ์ธ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค์ž…๋‹ˆ๋‹ค. 

๋Œ€๋ถ€๋ถ„ ์‚ฌ๋žŒ๋“ค์€ ์ด๋Ÿฐ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค: 

private boolean flag;

void A(){
	flag = true;
	//do something
	B();
}
void B(){
	C();
}
void C(){
	if(flag){
		//do something
	}
}

์ด ์˜ˆ์ œ์—์„œ, C()์˜ ์‹คํ–‰์„ ์ปจํŠธ๋กคํ•˜๊ธฐ ์œ„ํ•ด ์ „์—ญ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ณ€์ˆ˜๋Š” ๋ณ€ํ•  ์ˆ˜ ์žˆ๊ณ  A() ์•ˆ์—์„œ๋„ ๋ฐ”๋€” ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ•จ์ˆ˜ ์‹คํ–‰ ์ˆœ์„œ๊ฐ€ A()->B()->C()๋ผ๊ณ  ํ•ด๋„, C()๋Š” B()์— ๋Œ€ํ•ด ์ˆœ์ˆ˜ํ•œ ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ฉฐ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๋ฃฐ์—๋„ ์–ด๊ธ‹๋‚ฉ๋‹ˆ๋‹ค. 

์ฝ”๋“œ๋ฅผ ์‚ด์ง ๋ฐ”๊ฟ”๋ณผ๊นŒ์š”. 

void A(){
	boolean flag = true;
	//do something
	B(flag);
}
void B(boolean flag){
	C(flag);
}
void C(boolean flag){
	if(flag){
		//do something
	}
}

ํ›จ์”ฌ ๋‚ซ๋„ค์š”! B()์™€ C()๋Š” ๋”์ด์ƒ ์ „์—ญ๋ณ€์ˆ˜์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๊ณ , B()์™€ C()์˜ ๊ฒฐ๊ณผ๋Š” ์ˆœ์ˆ˜ํ•˜๊ฒŒ ๊ฐ๊ฐ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์˜ํ•ด์„œ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  “RxJava”์Šค๋Ÿฝ๊ฒŒ ๋ฐ”๊พผ๋‹ค๋ฉด 

public static void main(String[] args) {
	new Exe(true)
	.A()
	.B()
	.C();
}

static class Exe {
	boolean flag;
	public Exe(boolean flag){
		this.flag = flag;
		//do something
	}
	Exe A(){
		boolean flag = true;
		//do something
		return new Exe(flag);
	}
	Exe B(){
		return new Exe(flag);
	}
	Exe C(){
		if(flag){
			//do something
		}
		return new Exe(flag);
	}
}

์œ„ ์ฝ”๋“œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋” functionalํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š”์ง€ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค! 

๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์„œ ์˜๋ฌธ์ ์€ -> ์—ฌ์ „ํžˆ Exe ํด๋ž˜์Šค์— boolean ํ”Œ๋ž˜๊ทธ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค! ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™์— ์–ด๊ธ‹๋‚˜์ง€ ์•Š์„๊นŒ์š”? 

์Œ, ๊ทธ๋ ‡์ง„ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ ์ฒด์ธ์˜ ์‹คํ–‰์„ ํ†ต์ œํ•˜๊ธฐ ์œ„ํ•ด ๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š” ๋ณ€์ˆ˜๋ฅผ ์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™์— ์–ด๊ธ‹๋‚œ๋‹ค๋Š” ๊ฒƒ์„ ํ•ญ์ƒ ๊ธฐ์–ตํ•˜์„ธ์š”. ํ•˜์ง€๋งŒ ์ด ๋ง์ด ์ „์—ญ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์˜๋ฏธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ์œ„์˜ ์ฝ”๋“œ์—์„œ, A(), B(), C()๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค Exeํด๋ž˜์Šค์˜ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๊ฐ™์€ Exe ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋ง์€ ๋ฐ”๋กœ ์ด์ „์˜ Exe ์ธ์Šคํ„ด์Šค๋งŒ ํ˜„์žฌ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰์— ๊ด€์—ฌํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. 

๊ทธ๋Ÿผ ์ด์ œ Java8์˜ Stream API๋ฅผ ๋‹ค์‹œ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. map(), filter(), limit() ๊ฐ™์€ ์—ฐ์‚ฐ์ž๊ฐ€ ์ƒˆ๋กœ์šด “Stream”์„ ์ƒ์„ฑํ•ด ๋‹ค์Œ ์—ฐ์‚ฐ์ž์— ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค. ์ด์ „ ์—ฐ์‚ฐ์ž์— ์˜ํ•ด ๋งŒ๋“ค์–ด์ง„ ์ŠคํŠธ๋ฆผ์„ ๋ณ€๊ฒฝํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. 

public static void main(String[] args) {
		ArrayList<Integer> arrayList = new ArrayList<Integer>();
		arrayList.stream()
		         .map(integer -> integer + 1)
             //at this point, stream has already been modified by adding 1 to each element
		         .filter(integer -> integer < 10)
             //at this point, stream has already been modified by filtering out those numbers less than 10
		         .limit(10);
}

์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ part1 ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŠธ์—์„œ๋Š” RxJava์˜ ์—ฐ์‚ฐ์ž์™€ API๋ฅผ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.