๐ ๋ฐฐ๊ฒฝ
์ ํํ ์ค์ ๊ฐ ๊ณ์ฐ์ด ํ์ํด Java์ BigDecimal์ ์ฌ์ฉํด ๊ตฌํํ ํน์ ๋น์ฆ๋์ค ๋ก์ง์ด ์์๋ค.
ํด๋น ๋ก์ง์ ์กฐํ ๊ธฐ๋ฅ์ด ๋ ๊ฐ์ง๊ฐ ์๋๋ฐ, ํ๋๋ DB ๊ฐ์ ์กฐํํ๋ ํ์ฌ ์กฐํ, ๋ค๋ฅธ ํ๋๋ ๋ฏธ๋ ๋ ์ง๋ฅผ ์ ํํ๋ฉด ์์๋ ๊ฐ์ ๊ณ์ฐํด์ ๋ณด์ฌ์ฃผ๋ ๋ฏธ๋ ์กฐํ๊ฐ ์๋ค.
์ด๋ ๋ ๊ธฐํ์๊ฐ ํด๋น ๋น์ฆ๋์ค ๋ก์ง๊ณผ ๊ด๋ จ๋ ํ๋ฉด์ ์บก์ฒํด์ ๋ณด์ฌ์ฃผ๋ฉฐ, ์กฐํ์ ๊ณ์ฐ๋ ๊ฐ์ด ๋ง์ง ์๋๋ค๊ณ ํ๋ฉฐ ์๋ฌธ์ ์ ๊ธฐํ๋ค.
ํด๋น ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ๋์ ์๊ฒ ๋ ๋ถ๋ถ์ ๋ํด์ ๊ณต์ ํ๊ณ ์ ํ๋ค.
๐ ๋ฌธ์
ํ์ฌ ์กฐํ(DB ์กฐํ)๋ฅผ ํ๋ฉด 100.0 ๊ฐ์ด ์ถ๋ ฅ๋๊ณ , ๋ฏธ๋ ๋ ์ง๋ฅผ ์ ๋ ฅํ๊ณ ๊ณ์ฐํด์ ์กฐํํ๋ฉด 99.9๊ฐ ๋จ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
ํด๋น ๋ฌธ์ ๋ 100์ 3์ผ๋ก ๋๋๋ ๊ฒฝ์ฐ ๋ฐ์ํ๋๋ฐ, 100์ 3์ผ๋ก ๋๋๋ฉด 33.333333333...์ผ๋ก ๋์ค๊ฒ ๋๊ณ , 3๊ฐ๋ฅผ ๋ํ๋ฉด 99.999999999... ๊ฐ ๋๋ค. ๊ทธ๋ฐ๋ฐ DB ์กฐํ๋ 100.0์ด๊ณ ์ ๊ณ์ฐ ์กฐํ๋ 99.9์ธ ๊ฒ์ผ๊น?
๐ ์์ธ
MariaDB์ decimal(M, D)์ D๋ฅผ ์ด๊ณผํ ์ฒซ ๋ฒ์งธ ์๋ฆฟ์์์ ๋ฐ์ฌ๋ฆผ ์ฒ๋ฆฌํ์ง๋ง Java์ BigDecimal์ ๋ณ๋๋ก ์ฒ๋ฆฌํ์ง ์์๋ ๊ฒ์ด ๋ฌธ์ ์๋ค. (decimal(M,D)๋ M์ ์ ์ฒด ์๋ฆฟ์, D๋ ์์์ ์ดํ ์๋ฆฟ์๋ฅผ ๊ฐ์ง ์ซ์ํ์ ์๋ฏธํ๋ค.)
์๋ฅผ ๋ค์ด decimal(16,10)์ธ ๊ฒฝ์ฐ ์์์ ์ด 10์๋ฆฌ๋ฅผ ๋์ด๊ฐ๋ ์ฒซ ๋ฒ์งธ ์๋ฆฌ ์ฆ, 11๋ฒ์งธ ์๋ฆฌ ๊ฐ์์ ๋ฐ์ฌ๋ฆผ(5 ์ด์ ์ฌ๋ฆผ)์ด ๋๋ค.
ํด๋น ์ํฉ์ ๊ฐ๋จํ๊ฒ ํ ์คํธ๋ฅผ ๊ตฌ์ฑํด์ ์ฌ๊ตฌ์ฑํด๋ดค๋ค.
@Entity
@Table(name = "task")
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(precision = 16, scale = 10)
private BigDecimal value;
}
@SpringBootTest
public class TaskServiceTest {
@Autowired
TaskRepository taskRepository;
@Autowired
EntityManager entityManager;
@Test
@Transactional
void decimalIssue() {
Task task = Task.builder()
.value(new BigDecimal("99.99999999999"))
.build();
Task savedTask = taskRepository.save(task);
entityManager.clear();
Task result = taskRepository.findById(savedTask.getId()).get();
System.out.println(result);
}
}
์์๊ฐ 10 ์๋ฆฟ์๋ฅผ ๋์ด๊ฐ๋ ๊ฐ์ BigDecimal๋ก ์ ์ฅํ๊ณ , ์กฐํํด์ ํ์ธํด ๋ณธ๋ค.
ํ ์คํธ ๊ฒฐ๊ณผ๋ 100์ผ๋ก ๋์ค๋ ๊ฒ์ ์ ์ ์๋ค. ๋ํ, DB์๋ 100์ผ๋ก ์ ์ฅ๋์ด ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
ํ์ง๋ง Java BigDecimal์ ๋ณ๋๋ก Scale ๋ฐ RoundingMode๋ฅผ ์ค์ ํด์ฃผ์ง ์์ผ๋ฉด ์ฌ์ ํ 99.999999..๋ก ๊ฐ์ ๊ฐ์ง๊ณ ์๊ฒ ๋๋ค. ๋ง์ฝ DB์ ์ค์ ๋ decimal๊ณผ ๋ค๋ฅธ ์๋ฆฟ์์์ ๋ฐ์ฌ๋ฆผ ๋๋ ์ฌ๋ฆผ, ๋ด๋ฆผ ๋ฑ์ ์ฒ๋ฆฌ๋ฅผ ํ๊ฒ ๋๋ค๋ฉด DB์ ์ ์ฅ๋ ๊ฐ๊ณผ ๋ค๋ฅธ ๊ฐ์ด ์กฐํ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
๐ ํด๊ฒฐ
DB ์ ์ฑ ๊ณผ ๋์ผํ๊ฒ ๋ง์ถ์ด ๊ณ์ฐํ๋ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ผ๋ก ํด๊ฒฐํ๊ฒ ๋์๋ค. BigDecimal์ ์๋ฆฟ์๋ฅผ ๋์ด๊ฐ๋ฉด ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง ๊ฒฐ์ ํ ์ ์๋๋ก ํ๋ setScale(int newScale, RoundingMode roundingMode) ๋ฉ์๋๋ฅผ ์ง์ํ๋ค.
(RoundingMode๋ ๊ฐ์ด scale ๊ฐ์ ๋์ด๊ฐ ๊ฒฝ์ฐ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง ์ ํ๋ enum)
new BigDecimal("99.99999999999").setScale(10, RoundingMode.HALF_UP);
REFERENCE
https://mariadb.com/kb/en/decimal/
https://docs.oracle.com/javase/8/docs/api/java/math/RoundingMode.html
๋๊ธ