CleanCode TIL (2022.02.01)

Henry Choยท2022๋…„ 2์›” 1์ผ
0

๋…ธ๊ฐœ๋ถ

๋ชฉ๋ก ๋ณด๊ธฐ
12/31

DAY 12

๐Ÿ”–ย ์˜ค๋Š˜ ์ฝ์€ ๋ฒ”์œ„ : 6. ๊ฐ์ฒด์™€ ์ž๋ฃŒ๊ตฌ์กฐ(118~128p)


๐Ÿค“ย ์ฑ…์—์„œ ๊ธฐ์–ตํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ

  • ๋ณ€์ˆ˜๋ฅผ ๋น„๊ณต๊ฐœ(private)๋กœ ์ •์˜ โ†’ ์™ธ๋ถ€์— ์˜์กด์„ฑ์ด ์—†๊ฒŒ ํ•˜๋ฉด ๋ง˜๋Œ€๋กœ ๋ฐ”๊ฟ€์ˆ˜ ์žˆ๋‹ค
  • ์™œ get, set ์€ ํ•ญ์ƒ public ์ผ๊นŒ?

์ž๋ฃŒ ์ถ”์ƒํ™”

  • ์ž๋ฃŒ๋ฅผ ์„ธ์„ธํ•˜๊ฒŒ ๊ณต๊ฐœํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ์ถ”์ƒ์ ์ธ ๊ฐœ๋…์œผ๋กœ ํ‘œํ˜„ํ•˜๋Š” ํŽธ์ด ์ข‹๋‹ค
public class Point {
    public double x;
    public double y;
}

์ง๊ต์ขŒํ‘œ๊ณ„ ์ž„์„ ๋ฐ”๋กœ ์•Œ๊ฒŒ ๋˜๋ฒ„๋ฆฐ๋‹ค

๊ฐœ๋ณ„์  ์ขŒํ‘œ๊ฐ’์„ read, write ํ•˜๊ฒŒ ๊ฐ•์ œํ•œ๋‹ค

๊ตฌํ˜„์„ ๋…ธ์ถœํ•œ๋‹ค โ†’ ๋ณ€์ˆ˜๋ฅผ private ์œผ๋กœ ํ• ์ง€๋ผ๋„ ๊ฐ ๊ฐ’๋งˆ๋‹ค get, set์„ ์ œ๊ณตํ•˜๋ฉด ๊ตฌํ˜„์„ ์™ธ๋ถ€๋กœ ๋…ธ์ถœํ•˜๋Š” ์…ˆ

๋ณ€์ˆ˜ ์‚ฌ์ด์— ํ•จ์ˆ˜๋ผ๋Š” ๊ณ„์ธต์„ ๋„ฃ๋Š”๋‹ค๊ณ  ๊ตฌํ˜„์ด ๊ฐ์ถฐ์ง€๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์ถ”์ƒํ™”๊ฐ€ ํ•„์š”

public interface Point {
    double getX();
    double getY();
    void setCartesian(double x, double y);
    double getR();
    double getTheta();
    void setPolar(double r, double theta);
}

์ง๊ต, ๊ทน ์ขŒํ‘œ๊ณ„ ๋“ฑ์„ ๋ชฐ๋ผ๋„ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ž๋ฃŒ ๊ตฌ์กฐ๋ฅผ ๋ช…๋ฐฑํ•˜๊ฒŒ ํ‘œํ˜„

ํด๋ž˜์Šค ๋ฉ”๋”์Šค๊ฐ€ ์ ‘๊ทผ ์ •์ฑ…์„ ๊ฐ•์ œ

์ฝ์„ ๋•Œ๋Š” ๊ฐ ์ขŒํ‘œ ๊ฐœ๋ณ„๋กœ

์„ค์ •ํ•  ๋•Œ๋Š” ๋‘ ๊ฐ’์„ ํ•œ๋ฒˆ์— ์„ค์ •

์ž๋ฃŒ/๊ฐ์ฒด ๋น„๋Œ€์นญ

๊ฐ์ฒด vs ์ž๋ฃŒ๊ตฌ์กฐ ์„œ๋กœ ์ƒ๋ฐ˜๋˜๋Š” ์ •์˜

  • ๊ฐ์ฒด๋Š” ์ถ”์ƒํ™” ๋’ค๋กœ ์ž๋ฃŒ๋ฅผ ์ˆจ๊ธด ์ฑ„ ์ž๋ฃŒ๋ฅผ ๋‹ค๋ฃจ๋Š” ํ•จ์ˆ˜๋งŒ ๊ณต๊ฐœ
  • ์ž๋ฃŒ ๊ตฌ์กฐ๋Š” ์ž๋ฃŒ๋ฅผ ๊ทธ๋Œ€๋กœ ๊ณต๊ฐœํ•˜๋ฉฐ ๋ณ„๋‹ค๋ฅธ ํ•จ์ˆ˜๋Š” ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค.
public class Square {
    public Point topLeft;
    public double side;
}
public class Rectangle {
    public Point topLeft;
    public double height;
    public double width;
}
public class Circle {
    public Point center;
    public double radius;
}
public class Geometry {
    public final double PI = 3.141592653589793;
    public double area(Object shape) throws NoSuchShapeException {
        if (shape instanceof Square) {
            Square s = (Square) shape;
            return s.side * s.side;
        } else if (shape instanceof Rectangle) {
            Rectangle r = (Rectangle) shape;
            return r.height * r.width;
        } else if (shape instanceof Circle) {
            Circle c = (Circle) shape;
            return PI * c.radius * c.radius;
        }
        throw new NoSuchShapeException();
    }
}
  • ์ ˆ์ฐจ์ ์ธ ๋„ํ˜• (์ž๋ฃŒ๊ตฌ์กฐ)
  • Geometry ํด๋ž˜์Šค์— ๋‘˜๋ ˆ ๊ตฌํ•˜๋Š” perimeter() ํ•จ์ˆ˜ ์ถ”๊ฐ€ํ•ด๋„ ๋„ํ˜• ํด๋ž˜์Šค๋Š” ์˜ํ–ฅ์—†์Œ๐Ÿ‘
  • ๋ฐ˜๋Œ€๋กœ ์ƒˆ ๋„ํ˜•์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Geometry ํด๋ž˜์Šค ์†ํ•œ ํ•จ์ˆ˜ ๋ชจ๋‘ ๊ณ ์ณ์•ผํ•จ ๐Ÿ‘Ž
public class Square implements Shape {
    private Point topLeft;
    private double side;
    public double area() {
        return side * side;
    }
}
public class Rectangle implements Shape {
    private Point topLeft;
    private double height;
    private double width;
    public double area() {
        return height * width;
    }
}
public class Circle implements Shape {
    private Point center;
    private double radius;
    public final double PI = 3.141592653589793;
    public double area() {
        return PI * radius * radius;
    }
}
  • ๋‹คํ˜•์ ์ธ ๋„ํ˜• (๊ฐ์ฒด ์ง€ํ–ฅ)
  • area() ๋Š” ๋‹คํ˜• ๋ฉ”์„œ๋“œ, Geometry ํด๋ž˜์Šค๋Š” ํ•„์š” ์—†์Œ โ†’ ์ƒˆ ๋„ํ˜•์„ ์ถ”๊ฐ€ํ•ด๋„ ๊ธฐ์กด ํ•จ์ˆ˜์— ์˜ํ–ฅ ๋ฐ›์ง€ ์•Š์Œ ๐Ÿ‘
  • ์ƒˆ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์œผ๋ฉด? ๋ชจ๋“  ๋„ํ˜• ํด๋ž˜์Šค๋ฅผ ๊ณ ์ณ์•ผ ํ•จ ๐Ÿ‘Ž

์ •๋ฆฌ

์ ˆ์ฐจ์ ์ธ ์ฝ”๋“œ

์ƒˆ ํ•จ์ˆ˜ ์ถ”๊ฐ€ ์‰ฌ์›€

์ƒˆ ์ž๋ฃŒ ๊ตฌ์กฐ ์ถ”๊ฐ€ ์–ด๋ ค์›€

๊ฐ์ฒด ์ง€ํ–ฅ ์ฝ”๋“œ

์ƒˆ ํด๋ž˜์Šค ์ถ”๊ฐ€ ์‰ฌ์›€

์ƒˆ ํ•จ์ˆ˜ ์ถ”๊ฐ€ ์–ด๋ ค์›€

๋””๋ฏธํ„ฐ ๋ฒ•์น™

  • ๋ชจ๋“ˆ์€ ์ž์‹ ์ด ์กฐ์ž‘ํ•˜๋Š” ๊ฐ์ฒด์˜ ์†์‚ฌ์ •์„ ๋ชฐ๋ผ์•ผ ํ•œ๋‹ค
  • ๊ฐ์ฒด๋Š” ์ž๋ฃŒ๋ฅผ ์ˆจ๊ธฐ๊ณ  ํ•จ์ˆ˜๋ฅผ ๊ณต๊ฐœ โ†’ ์กฐํšŒ ํ•จ์ˆ˜๋กœ ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ๊ณต๊ฐœํ•˜๋ฉด ์•ˆ๋œ๋‹ค
  • ํด๋ž˜์Šค C์˜ ๋ฉ”์„œ๋“œ f๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋งŒ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค
    • ํด๋ž˜์Šค C

    • f๊ฐ€ ์ƒ์„ฑํ•œ ๊ฐ์ฒด

    • f์˜ ์ธ์ˆ˜๋กœ ๋„˜์–ด์˜จ ๊ฐ์ฒด

    • C ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜์— ์ €์žฅ๋œ ๊ฐ์ฒด

      ๋‹จ ์œ„ ๊ฐ์ฒด์—์„œ ํ—ˆ์šฉ๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋Š” ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ ๋œ๋‹ค

      final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

      ๋””๋ฏธํ„ฐ ๋ฒ•์น™ ์œ„๋ฐ˜์‚ฌ๋ก€

๊ธฐ์ฐจ ์ถฉ๋Œ

  • ์œ„ ์œ„๋ฐ˜์‚ฌ๋ก€๋Š” ๊ธฐ์ฐจ์ถฉ๋Œ(train wreck) ์ด๋ผ ๋ถ€๋ฅด๋ฏ€๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝ
    Options opts = ctxt.getOptions();
    File scratchDir = opts.getScratchDir();
    final String outputDir = scratchDir.getAbsolutePath();
    ctxt, Options, ScratchDir ์ด ๊ฐ์ฒด๋ฉด ๋””๋ฏธํ„ฐ ๋ฒ•์น™ ์œ„๋ฐ˜ ์ž๋ฃŒ ๊ตฌ์กฐ์ด๋ฉด ๋””๋ฏธํ„ฐ ๋ฒ•์น™ ์ ์šฉ ์•ˆ๋Œ
  • ํ˜ผ๋ž€์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝ
final String outputDir = ctxt.options.scratchDir.absolutePath;
  • ์ž๋ฃŒ ๊ตฌ์กฐ๋Š” ๋ฌด์กฐ๊ฑด ํ•จ์ˆ˜ ์—†์ด ๊ณต๊ฐœ ๋ณ€์ˆ˜๋งŒ ์ž‘์„ฑ, ๊ฐ์ฒด๋Š” ๋น„๊ณต๊ฐœ ๋ณ€์ˆ˜์™€ ๊ณต๊ฐœ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑ
  • ํ•˜์ง€๋งŒ ๋‹จ์ˆœํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์—๋„ ์กฐํšŒ ํ•จ์ˆ˜์™€ ์„ค์ • ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋ผ ์š”๊ตฌํ•˜๋Š” ํ”„๋ ˆ์ž„ ์›Œํฌ๋‚˜ ํ‘œ์ค€์ด ์žˆ๊ธด ํ•จ (eg. bean)

์žก์ข… ๊ตฌ์กฐ

  • ์ ˆ๋ฐ˜์€ ๊ฐ์ฒด, ์ ˆ๋ฐ˜์€ ์ž๋ฃŒ ๊ตฌ์กฐ์ธ ์žก์ข… ๊ตฌ์กฐ
  • ํ•จ์ˆ˜ ์ถ”๊ฐ€๋„ ์–ด๋ ต๊ณ  ์ž๋ฃŒ ๊ตฌ์กฐ ์ถ”๊ฐ€๋„ ์–ด๋ ค์šด ์–‘์ชฝ ๋‹จ์ ์„ ๋ชจ๋‘ ๊ฐ€์ง€๋ฏ€๋กœ ์ตœ๋Œ€ํ•œ ํ”ผํ•œ๋‹ค.

๊ตฌ์กฐ์ฒด ๊ฐ์ถ”๊ธฐ

  • ์œ„ ์˜ˆ์ œ์—์„œ ctxt, options, scratchDir์ด ์ง„์งœ ๊ฐ์ฒด๋ผ๋ฉด? ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€๊ฒฝ ์‹œ๋„
    ctxt.getAbsolutePathOfScratchDirectoryOption(); 
    ctxt ๊ฐ์ฒด์— ๊ณต๊ฐœํ•ด์•ผ ํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์ง„๋‹ค
    ctx.getScratchDirectoryOption().getAbsolutePath()
    getScratchDirectoryOption๊ฐ€ ์ž๋ฃŒ ๊ตฌ์กฐ๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋Š” ๊ฐ€์ •์ด๋‹ค
  • ์ž„์‹œ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ชฉ์ ์ด๋ฏ€๋กœ ctxt ์— ์ž„์‹œํŒŒ์ผ ์ƒ์„ฑ ์‹œํ‚จ๋‹ค
    BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);
    ctxt๋Š” ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ๋“œ๋Ÿฌ๋‚ด์ง€ ์•Š์œผ๋ฉฐ, ๋ชจ๋“ˆ์—์„œ ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ์ž์‹ ์ด ๋ชฐ๋ผ์•ผํ•˜๋Š” ์—ฌ๋Ÿฌ ๊ฐ์ฒด๋ฅผ ํƒ์ƒ‰ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. โ†’ ๋””๋ฏธํ„ฐ ๋ฒ•์น™์„ ์œ„๋ฐ˜ํ•˜์ง€ ์•Š์Œ

์ž๋ฃŒ ์ „๋‹ฌ ๊ฐ์ฒด DTO

  • ์ž๋ฃŒ ๊ตฌ์กฐ์ฒด์˜ ์ „ํ˜•์ ์ธ ํ˜•ํƒœ๋Š” ๊ณต๊ฐœ ๋ณ€์ˆ˜๋งŒ ์žˆ๊ณ  ํ•จ์ˆ˜๊ฐ€ ์—†๋Š” ํด๋ž˜์Šค โ†’ Data Transfer Object
  • DB์™€ ํ†ต์‹ ํ•˜๊ฑฐ๋‚˜ ์†Œ์ผ“์—์„œ ๋ฐ›์€ ๋ฉ”์‹œ์ง€์˜ ๊ตฌ๋ฌธ์„ ๋ถ„์„ํ•  ๋•Œ ์œ ์šฉ
  • DB์— ์ €์žฅ๋œ ๊ฐ€๊ณต๋˜์ง€ ์•Š์€ ์ •๋ณด๋ฅผ APP Code ์—์„œ ์‚ฌ์šฉํ•  ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋‹จ๊ณ„์—์„œ ๊ฐ€์žฅ ์ฒ˜์Œ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ์ฒด

bean ๊ตฌ์กฐ

  • ์ข€ ๋” ์ผ๋ฐ˜์ ์ธ DTO ์˜ ํ˜•ํƒœ๋กœ์„œ private ๋ณ€์ˆ˜๋ฅผ ์กฐํšŒ/์„ค์ • ํ•จ์ˆ˜๋กœ ์กฐ์ž‘
  • ์‚ฌ์ด๋น„ ์บก์Šํ™”?
public class Address {
    private String street;
    private String streetExtra;
    private String city;
    private String state;
    private String zip;
    public Address(String street, String streetExtra,
        String city, String state, String zip) {
        this.street = street;
        this.streetExtra = streetExtra;
        this.city = city;
        this.state = state;
        this.zip = zip;
    }
    public String getStreet() {
        return street;
    }
    public String getStreetExtra() {
        return streetExtra;
    }
    public String getCity() {
        return city;
    }
    public String getState() {
        return state;
    }
    public String getZip() {
        return zip;
    }
}

ํ™œ์„ฑ ๋ ˆ์ฝ”๋“œ

  • DTO์˜ ํŠน์ˆ˜ํ•œ ํ˜•ํƒœ
  • ๊ณต๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์žˆ๊ฑฐ๋‚˜ ๋น„๊ณต๊ฐœ ๋ณ€์ˆ˜์— ์กฐํšŒ/์„ค์ • ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š” ์ž๋ฃŒ ๊ตฌ์กฐ์ด์ง€๋งŒ save ๋‚˜ find ์™€ ๊ฐ™์€ ํƒ์ƒ‰ ํ•จ์ˆ˜๋„ ์ œ๊ณต
  • ํ™œ์„ฑ ๋ ˆ์ฝ”๋“œ๋Š” ์—ฌ์ „ํžˆ ์ž๋ฃŒ ๊ตฌ์กฐ๋กœ ์ทจ๊ธ‰๋˜๋ฏ€๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์„ ๋‹ด์œผ๋ฉด์„œ ๋‚ด๋ถ€ ์ž๋ฃŒ๋ฅผ ์ˆจ๊ธฐ๋Š” ๊ฐ์ฒด๋Š” ๋”ฐ๋กœ ์ƒ์„ฑํ•˜๋ผ

๊ฒฐ๋ก 

  • ๊ฐ์ฒด๋Š” ๋™์ž‘์„ ๊ณต๊ฐœํ•˜๊ณ  ์ž๋ฃŒ๋ฅผ ์ˆจ๊ธด๋‹ค โ†’ ์ƒˆ ๊ฐ์ฒด ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋Š” ์‰ฝ์ง€๋งŒ ์ƒˆ ๋™์ž‘์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค
  • ์ž๋ฃŒ๊ตฌ์กฐ๋Š” ๋™์ž‘ (๊ฑฐ์˜)์—†์ด ์ž๋ฃŒ๋ฅผ ๋…ธ์ถœํ•œ๋‹ค โ†’ ์ƒˆ ๋™์ž‘์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋Š” ์‰ฝ์ง€๋งŒ ์ƒˆ ์ž๋ฃŒ ๊ตฌ์กฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค
  • ์šฐ์ˆ˜ํ•œ ๊ฐœ๋ฐœ์ž๋Š” ํŽธ๊ฒฌ ์—†์ด ๋‘ ๊ฒฝ์šฐ๋ฅผ ๋ชจ๋‘ ๊ณ ๋ คํ•˜๊ณ  ์ตœ์ ์˜ ํ•ด๊ฒฐ์ฑ…์„ ์„ ํƒ ํ•œ๋‹ค

๐Ÿค”ย ๋– ์˜ค๋ฅด๋Š” ์ƒ๊ฐ

  • ์ด ์ฑ…์— ๊ฐ์‚ฌํ•˜๋‹ค. ๋‹ค๋ฅธ ์ž๋ฐ” ๊ฐœ๋ฐœ์ž๋“ค์€ ๊ฐ์ฒด์ง€ํ–ฅ์ด ์ตœ๊ณ ๋ผ๊ณ ๋งŒ ํ•˜๋Š”๋ฐ ๊ฐ์ž ๋‹ค ์“ฐ์ž„์ด ์žˆ๋‹ค๊ณ  ํ•ด์ฃผ์…จ๋‹ค!

๐Ÿ”Žย ์งˆ๋ฌธ

  • BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName); ๊ฐ€ ์™œ ๋””๋ฏธํ„ฐ ๋ฒ•์น™์„ ์œ„๋ฐฐํ•˜์ง€ ์•Š๊ฒŒ ๋ฐ”๊พผ ๊ฒƒ์ธ๊ฐ€? dir ์–ป์–ด์„œ ๊บผ๋‚ด๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์•ˆ์—์„œ ์ž„์‹œํŒŒ์ผ ์ƒ์„ฑ ์•„์˜ˆ ํ•ด๋ฒ„๋ฆฌ๋‹ˆ๊นŒ ๊ดœ์ฐฎ๋‹ค๋Š”๊ฑด๊ฐ€ ๊ทธ๋ž˜๋„ ctxt ํ•˜๋‚˜์— ๋ชจ๋“  ๋ฉ”์„œ๋“œ๊ฐ€ ๊ณต๊ฐœ๋˜๋ฏ€๋กœ ์•ˆ์ข‹์€๊ฑด ์—ฌ์ „ํ•˜์ง€ ์•Š๋‚˜?

๐Ÿ“ย ์†Œ๊ฐ 3์ค„ ์š”์•ฝ

  • ๊ฐ์ฒด๋Š” ์ƒˆ ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋Š” ์‰ฝ์ง€๋งŒ ์ƒˆ ๋™์ž‘์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค
  • ์ž๋ฃŒ ๊ตฌ์กฐ๋Š” ์ƒˆ ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•˜๋Š”๊ฑด ์–ด๋ ต์ง€๋งŒ ์ƒˆ ๋™์ž‘์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋Š” ์‰ฝ๋‹ค
  • ์šฐ์ˆ˜ํ•œ ๊ฐœ๋ฐœ์ž๋Š” ๋ฌด์กฐ๊ฑด ๊ฐ์ฒด์ง€ํ–ฅ์ด ์•„๋‹ˆ๋ผ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ์„œ๋Š” ์ ˆ์ฐจ์ง€ํ–ฅ์˜ ์ž๋ฃŒ๊ตฌ์กฐ๋„ ์ž˜ ์“ด๋‹ค
profile
Full stack tech visionary

0๊ฐœ์˜ ๋Œ“๊ธ€