Efficiently using a serial inteface
The nastiest part in what you describe is likely to be the debouncing on the button, rather than the serial communication.

One easy way out is to use some form of bounceless implementation, like an optical coupler. Another is to use HW debouncing, like the MAX 6816 family.

Then you can use a loop where you alternate between serial communications and checks for the status of the buttons.

Or you can rely on interrupts for the buttons, but it's more complex and might be overkill.